【问题标题】:Xml serialization of derived classes not known until runtime直到运行时才知道派生类的 XML 序列化
【发布时间】:2014-08-12 09:57:32
【问题描述】:

我目前正在实现一个工具,用于处理符合标准 Xml 架构的 Xml 文件。到目前为止,我已经从使用 Xml 序列化属性的模式生成了类,并且我正在使用 XmlSerializer 进行反序列化,并且使用正常工作的标准模式。

但是,架构具有各种扩展点,并且这些扩展直到运行时才知道。一个简化的示例可能如下所示:

// ==== In main assembly ====

public class DeclarationRealisation 
{
    public DataType dataType { get; set; }
}

[XmlInclude(typeof(String)), XmlInclude(typeof(Integer))]  
public abstract class DataType : ExtensionInterface { /* ... */ }

// ==== In extension assembly ====

public class Struct : DataType { /* ... */ }

Struct 有自己的 xsd 文件导入主 xsd。
如果我将Struct 放入主程序集中,即使没有XmlInclude 属性(可能sgen 这样做),它也能正常工作。但是如果我将Struct 放入它自己的程序集中(我们将处理扩展的方式),我会得到一个InvalidOperation 说明“指定的类型未被识别”。由于扩展是在运行时加载的,我不能使用XmlInclude

我已经阅读了实现IXmlSerializable 的可能性,但我宁愿避免手动执行此操作,因为它有很多类。所以,我的问题是:

  • 有没有办法实现这种可扩展性而无需实现IXmlSerializable

  • 如果没有,是否有工具/代码生成器可以处理此类场景(通过生成界面或任何其他方式)?

注意:我知道您可以在创建 XmlSerializers 时指定额外的类型,但是自动生成的 XmlSerializers 的启动命中(在我的情况下 > 3 秒没有扩展)对于我的用例来说有点多,我想避免它。

编辑:考虑一下,如果在第一次启动期间发生一次性能下降,这可能是可以接受的。有没有办法在运行时生成序列化程序集?我曾经听说 sgen 只是一些框架类的命令行客户端,但现在找不到了。

【问题讨论】:

    标签: c# .net xml xsd


    【解决方案1】:

    可以在XmlSerializer constructor的第二个参数中指定所有类型。

    var serializer = new XmlSerializer(typeof(DeclarationRealisation), new Type[] { typeof(Struct) });
    
    DeclarationRealisation result;
    using (var reader = new StreamReader("Test.xml"))
    {
        result = serializer.Deserialize(reader) as DeclarationRealisation;
    }
    

    【讨论】:

    • 如果 OP 将 Struct 类打包到单独的程序集中,则可以在运行时使用 MEF 和反射获得的 Type 加载它。
    • 感谢您的回答,我知道这种可能性,但不幸的是,使用该构造函数会导致运行时生成序列化程序集,这在我的情况下是一个相当大的性能损失。
    【解决方案2】:

    好的,事实证明> 3 秒的性能损失是因为我使用了通用的Deserialize<T> 方法来按需生成匹配的序列化程序。即使我缓存了它们,每次我传递一个新类型时都会生成一个新的序列化程序,包括代码生成和编译,这些都是累积的。

    所以现在我创建了一个List<Type>,包含我将使用此方法反序列化的所有类型,并向其中添加所有扩展类型,然后我一次性生成所有必需的序列化程序:

    var deserializedTypes = new List<Type> {
        typeof(DeclarationRealisation) // ...etc...
    };
    
    foreach (var extensionType in extensionTypes) { // Includes Struct and more
        deserializedTypes.Add(extensionType);
    }
    
    var generatedSerializers = XmlSerializer.FromTypes(deserializedTypes.ToArray());
    for (int i = 0; i < generatedSerializers.Length; i++) {
        var xmlType = deserializedTypes[i];
        var serializer = generatedSerializers[i];
        serializers[xmlType] = serializer;
    }
    

    使用这种方法,启动命中减少到 ~0.45 秒,这在某种程度上是可以接受的。

    如果我现在能够以某种方式保存生成的程序集以在后续启动时使用它,那将是非常棒的,但似乎 InternalAssemblyBuilder 类型的生成程序集无法保存它(我会感谢任何提示这个)。但就目前而言,这已经足够了。

    【讨论】:

      猜你喜欢
      • 2014-12-13
      • 2011-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多