C# 将xml反序列化为具有不同层次结构的类?

C# 将xml反序列化为具有不同层次结构的类?,c#,xml,xmlserializer,C#,Xml,Xmlserializer,这将把xml样本反序列化到“XmlModel”类中 我使用的是遗留代码,除了更改XmlAttributes之外,我无法更改XmlModel类。问题是:实际的Xml没有“foo”节点: stringxml=@” 1. 2. "; 所以现在我不得不让反序列化程序吞下这个xml和输出类型XmlModel。如果没有Xslt预处理或其他更复杂的方法,这是可能的吗?如果您愿意使用另一种反序列化方法,这将起作用。它应该与XmlSerializer一样快(如果不是更快的话)。它只需在原始xml上打开一个Xml

这将把xml样本反序列化到“XmlModel”类中

我使用的是遗留代码,除了更改XmlAttributes之外,我无法更改XmlModel类。问题是:实际的Xml没有“foo”节点:

stringxml=@”
1.
2.
";

所以现在我不得不让反序列化程序吞下这个xml和输出类型XmlModel。如果没有Xslt预处理或其他更复杂的方法,这是可能的吗?

如果您愿意使用另一种反序列化方法,这将起作用。它应该与
XmlSerializer
一样快(如果不是更快的话)。它只需在原始xml上打开一个
XmlReader
,移动到第一个“数据”元素,将数据转储到列表中,然后填充并从中返回
XmlModel

LINQPad文件可用

公共XmlModel GetXmlModel() { 字符串xml=@“ 1. 2. "; 使用(var reader=XmlReader.Create(newstringreader(xml))) { reader.MoveToContent(); var data=新列表(); while(reader.Read()) { if(reader.NodeType==XmlNodeType.Element) { var element=XNode.ReadFrom(reader)作为XElement; 开关(element.Name.LocalName) { 案例“酒吧”: { 数据。添加(元素。值); 打破 } } } } 返回新的XmlModel(){Foo=data}; } } 如果
bar
类不仅仅是一个简单的内在类型,例如
string
,那么这显然会变得更复杂,您可以使用它为
XmlModel
指定备用XML属性,然后通过执行以下操作来使用这些属性:

var serializer = new XmlSerializer(typeof(XmlModel), overrides).
但是,请注意来自的以下警告:

为了提高性能,XML序列化基础结构动态生成程序集以序列化和反序列化指定类型。基础结构查找并重用这些程序集。此行为仅在使用以下构造函数时发生:

XmlSerializer.XmlSerializer(类型)

XmlSerializer.XmlSerializer(类型, (字符串)

如果使用任何其他构造函数,将生成同一程序集的多个版本,并且从未卸载,这将导致内存泄漏和性能低下。最简单的解决方案是使用前面提到的两个构造函数之一。否则,必须将程序集缓存在哈希表中

以下静态类创建并缓存2个序列化程序,一个用于
XmlModel
的“当前”版本,另一个用于
元素缺少外部容器元素的“备用”版本:

public static class XmlModelSerializer<TRoot>
{
    static XmlSerializer alternateSerializerInstance;
    static XmlSerializer currentSerializerInstance;

    public static XmlSerializer AlternateSerializerInstance { get { return alternateSerializerInstance; } }

    public static XmlSerializer CurrentSerializerInstance { get { return currentSerializerInstance; } }

    static XmlModelSerializer()
    {
        XmlAttributes alternateAttributes = new XmlAttributes
        {
            XmlElements = { new XmlElementAttribute("bar") },
        };
        XmlAttributeOverrides alternateOverrides = new XmlAttributeOverrides();
        alternateOverrides.Add(typeof(XmlModel), "Foo", alternateAttributes);
        alternateSerializerInstance = new XmlSerializer(typeof(TRoot), alternateOverrides);

        XmlAttributes currentAttributes = new XmlAttributes
        {
            XmlArray = new XmlArrayAttribute("foo"),
            XmlArrayItems = { new XmlArrayItemAttribute("bar") },
        };
        XmlAttributeOverrides currentOverrides = new XmlAttributeOverrides();
        currentOverrides.Add(typeof(XmlModel), "Foo", currentAttributes);
        currentSerializerInstance = new XmlSerializer(typeof(TRoot), currentOverrides);
    }
}
您只需执行以下操作:

var dS = XmlModelSerializer<XmlModel>.AlternateSerializerInstance;
using (var reader = new StringReader(xml))
{
    return (XmlModel) dS.Deserialize(reader);
}
var dS=XmlModelSerializer.AlternateSerializerInstance;
使用(var reader=newstringreader(xml))
{
返回(XmlModel)dS.反序列化(读取器);
}

显示两种格式的反序列化的示例。

您可以反序列化为与XML匹配的格式,然后使用类似于“升级”的内容。不是超级性能,但我不知道你在这方面的要求是什么,这可能是可以接受的。你还有什么其他限制?您可以使用不同的反序列化方法吗?@Bradley Uffner使用代理类+自动映射是一种选择,但我认为这需要付出一些努力+性能损失(比XSLT prrprocessor更重要)。另一种序列化程序是我没有考虑过的选项,如果仅仅是XmlAttribute技巧不起作用(我希望它们会起作用),它可能是一种解决方案。使用
XmlSerializer
Attributes
可能有一些内置的方法来实现这一点。这是.NET的一个领域,我还没有深入研究过,所以我不确定。我只是想找到适合您的选项。遗留代码可能是一成不变的,但Xml肯定不是。您是否考虑过在尝试反序列化之前临时修改Xml(添加Foo根节点)?否则,在我看来,您将需要一个自定义反序列化程序。您的答案看起来好像是从中改编的,但不幸的是,MSDN代码有一个bug——它在XML未缩进时跳过元素。有关分析,请参阅。就我个人而言,我建议只加载到
XElement
中,并在内存中执行所有操作,正是因为直接使用
XmlReader
非常繁琐。我承认,我曾浏览过该示例以供参考。您的建议也是我的首选方式,但我一直在努力使事情尽可能快,因为op在讨论中提到了性能。这不仅表明在公共列表Foo{get;set;}上面使用[XmlElement(“bar”)]将省略“Foo”节点,它还展示了如何在两个反序列化(grand)之间动态切换。
public XmlModel GetXmlModel()
{
    string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
        <root>
                <bar>1</bar>
                <bar>2</bar>
        </root>";
    using (var reader = XmlReader.Create(new StringReader(xml)))
    {
        reader.MoveToContent();
        var data = new List<string>();
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                var element = XNode.ReadFrom(reader) as XElement;
                switch (element.Name.LocalName)
                {
                    case "bar":
                        {
                            data.Add(element.Value);
                            break;
                        }
                }
            }
        }
        return new XmlModel() { Foo = data };
    }
}
var serializer = new XmlSerializer(typeof(XmlModel), overrides).
public static class XmlModelSerializer<TRoot>
{
    static XmlSerializer alternateSerializerInstance;
    static XmlSerializer currentSerializerInstance;

    public static XmlSerializer AlternateSerializerInstance { get { return alternateSerializerInstance; } }

    public static XmlSerializer CurrentSerializerInstance { get { return currentSerializerInstance; } }

    static XmlModelSerializer()
    {
        XmlAttributes alternateAttributes = new XmlAttributes
        {
            XmlElements = { new XmlElementAttribute("bar") },
        };
        XmlAttributeOverrides alternateOverrides = new XmlAttributeOverrides();
        alternateOverrides.Add(typeof(XmlModel), "Foo", alternateAttributes);
        alternateSerializerInstance = new XmlSerializer(typeof(TRoot), alternateOverrides);

        XmlAttributes currentAttributes = new XmlAttributes
        {
            XmlArray = new XmlArrayAttribute("foo"),
            XmlArrayItems = { new XmlArrayItemAttribute("bar") },
        };
        XmlAttributeOverrides currentOverrides = new XmlAttributeOverrides();
        currentOverrides.Add(typeof(XmlModel), "Foo", currentAttributes);
        currentSerializerInstance = new XmlSerializer(typeof(TRoot), currentOverrides);
    }
}
<root>
    <bar>1</bar>
    <bar>2</bar>
</root>
var dS = XmlModelSerializer<XmlModel>.AlternateSerializerInstance;
using (var reader = new StringReader(xml))
{
    return (XmlModel) dS.Deserialize(reader);
}