将嵌套数组序列化为 CodeDOM 树





5.00/5 (2投票s)
一个简单的辅助类,用于辅助代码生成
引言
这只是一个简单的辅助工具,用于将数据结构序列化到 CodeDOM。我将其提供给网站,作为一个可以直接复制粘贴使用的类。它所做的就是接收一个数据结构,包括数组和嵌套数组,并创建一个 CodeDOM 表达式树,该树可用于重新实例化该数据结构。
我在我的代码生成项目中使用了它的一个变体,它极大地帮助了这项任务。事实上,对于像解析表和有限状态机这样的“表格驱动”代码,这使得代码生成几乎是自动化的。
Using the Code
要使用此代码,您只需调用
var exp=CodeDomUtility.Serialize(myValue);
然后就会创建一个某种类型的 CodeExpression
,它可以用来重新实例化它。这适用于标量/原始值和数组,包括嵌套数组。它适用于泛型类型,但由于 Microsoft 的 VBCodeProvider
中存在一个明显的错误,它在所有情况下都无法正确生成包含数组的泛型类型。
我将代码作为复制粘贴提供,因为它非常简单,实际上不需要下载。
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Reflection
// ... remember to put it in your namespace!
static partial class CodeDomUtility
{
static CodeExpression _SerializeArray(Array arr)
{
if (1 == arr.Rank && 0 == arr.GetLowerBound(0))
{
var result = new CodeArrayCreateExpression( arr.GetType());
foreach (var elem in arr)
result.Initializers.Add(Serialize(elem));
return result;
}
throw new NotSupportedException("Only SZArrays can be serialized to code.");
}
public static CodeExpression Serialize(object val)
{
if (null == val)
return new CodePrimitiveExpression(null);
if (val is bool ||
val is string ||
val is short ||
val is ushort ||
val is int ||
val is uint ||
val is ulong ||
val is long ||
val is byte ||
val is sbyte ||
val is float ||
val is double ||
val is decimal ||
val is char)
{
return new CodePrimitiveExpression(val);
}
if (val is Array && 1 == ((Array)val).Rank && 0 == ((Array)val).GetLowerBound(0))
{
return _SerializeArray((Array)val);
}
var conv = TypeDescriptor.GetConverter(val);
if (null != conv)
{
if (conv.CanConvertTo(typeof(InstanceDescriptor)))
{
var desc = conv.ConvertTo
(val, typeof(InstanceDescriptor)) as InstanceDescriptor;
if (!desc.IsComplete)
throw new NotSupportedException(
string.Format(
"The type \"{0}\" could not be serialized.",
val.GetType().FullName));
var ctor = desc.MemberInfo as ConstructorInfo;
if (null != ctor)
{
var result = new CodeObjectCreateExpression(ctor.DeclaringType);
foreach (var arg in desc.Arguments)
result.Parameters.Add(Serialize(arg));
return result;
}
throw new NotSupportedException(
string.Format(
"The instance descriptor for type \"{0}\" is not supported.",
val.GetType().FullName));
}
else
{
// we special case for KeyValuePair types.
if (val.GetType().GetGenericTypeDefinition()==typeof(KeyValuePair<,>))
{
// TODO: Find a workaround for the bug with VBCodeProvider
// may need to modify the reference source
var kvpType = new CodeTypeReference(typeof(KeyValuePair<,>));
foreach (var arg in val.GetType().GetGenericArguments())
kvpType.TypeArguments.Add(arg);
var result = new CodeObjectCreateExpression(kvpType);
for(int ic= kvpType.TypeArguments.Count,i = 0;i<ic;++i)
{
var prop = val.GetType().GetProperty(0==i?"Key":"Value");
result.Parameters.Add(Serialize(prop.GetValue(val)));
}
return result;
}
throw new NotSupportedException(
string.Format("The type \"{0}\" could not be serialized.",
val.GetType().FullName));
}
}
else
throw new NotSupportedException(
string.Format(
"The type \"{0}\" could not be serialized.",
val.GetType().FullName));
}
}
只需将此代码放入您的代码中,并如前所示调用它。您可以使用它作为 CodeMemberField
(通常是 static
)中的 InitExpression
,以将其作为实例获取回来。
关注点
发现 Microsoft 代码中的错误令人失望。
除此之外,请注意代码中对 InstanceDescriptor
的使用。这样您就可以告诉序列化器如何序列化您创建的类和结构。在 Google 上搜索更多信息,因为它超出了本文的范围,但 Microsoft 在其自身代码中使用它来支持 devstudio 中的可视化代码设计。
历史
- 2017 年 7 月 25 日 - 初始提交