65.9K
CodeProject 正在变化。 阅读更多。
Home

将嵌套数组序列化为 CodeDOM 树

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019 年 7 月 26 日

CPOL

1分钟阅读

viewsIcon

4263

一个简单的辅助类,用于辅助代码生成

引言

这只是一个简单的辅助工具,用于将数据结构序列化到 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 日 - 初始提交
© . All rights reserved.