简体   繁体   English

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

[英]Deserialize xml into a class with different hierarchy?

This will deserialize an xml sample into the "XmlModel" class.这会将 xml 示例反序列化为“XmlModel”类。

using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace XmlTest
{
    public class DeserializeXml
    {
        public XmlModel GetXmlModel()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
            <root>
                <foo>
                    <bar>1</bar>
                    <bar>2</bar>
                </foo>
            </root>";

            var dS = new XmlSerializer(typeof(XmlModel));

            var m = new XmlModel();
            using (var reader = new StringReader(xml))
            {
                return (XmlModel) dS.Deserialize(reader);
            }
        }
    }

    [XmlRoot("root")]
    public class XmlModel
    {
        [XmlArray("foo")]
        [XmlArrayItem("bar")]
        public List<string> Foo { get; set; }
    }
}

This will get the model:这将得到模型:

var d = new DeserializeXml();
result = d.GetXmlModel();

I am working with legacy code and I cannot make changes to the XmlModel class other than changing the XmlAttributes.我正在处理遗留代码,除了更改 XmlAttributes 之外,我无法对 XmlModel 类进行更改。 Here is the problem: the actual Xml has no "foo" node:这是问题所在:实际的 Xml 没有“foo”节点:

string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<root>
    <bar>1</bar>
    <bar>2</bar>
</root>";

So now I am stuck with the task of making the deserializer swallow this xml and output type XmlModel.所以现在我被困在让反序列化器吞下这个 xml 和输出类型 XmlModel 的任务中。 Is this possible without Xslt preprocessing or other more complicated methods?如果没有 Xslt 预处理或其他更复杂的方法,这可能吗?

If you are open to an alternate method of deserialization, this will work.如果您对反序列化的替代方法持开放态度,这将起作用。 It should be just as fast, if not faster, than the XmlSerializer .它应该和XmlSerializer一样快,如果不是更快的话。 It simply opens an XmlReader on the raw xml, moves to the first "data" element, dumps the data in to a list, then populates and returns your XmlModel from it.它只是在原始 xml 上打开一个XmlReader ,移动到第一个“数据”元素,将数据转储到列表中,然后填充并XmlModel返回您的XmlModel

LINQPad file available here .此处提供 LINQPad 文件。

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 };
    }
}

This obviously gets more complex if your bar class is more than a simple intrinsic type, like string .如果您的bar类不仅仅是一个简单的内在类型,例如string ,这显然会变得更加复杂。

You can use XmlAttributeOverrides to specify alternate XML attributes for your XmlModel , then construct an XmlSerializer using those attributes by doing:您可以使用XmlAttributeOverrides为您的XmlModel指定备用 XML 属性,然后通过执行以下操作使用这些属性构造XmlSerializer

var serializer = new XmlSerializer(typeof(XmlModel), overrides).

However, note the following warning from the documentation :但是,请注意文档中的以下警告:

To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types.为了提高性能,XML 序列化基础结构动态生成程序集以序列化和反序列化指定的类型。 The infrastructure finds and reuses those assemblies.基础结构查找并重用这些程序集。 This behavior occurs only when using the following constructors:此行为仅在使用以下构造函数时发生:

XmlSerializer.XmlSerializer(Type) XmlSerializer.XmlSerializer(类型)

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

If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance.如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。 The easiest solution is to use one of the previously mentioned two constructors.最简单的解决方案是使用前面提到的两个构造函数之一。 Otherwise, you must cache the assemblies in a Hashtable...否则,您必须将程序集缓存在哈希表中...

The following static class creates and caches 2 serializers, one for the "current" version of XmlModel , and one for the "alternate" version in which the <bar> elements lack an outer container element:以下静态类创建并缓存 2 个序列化程序,一个用于XmlModel的“当前”版本,另一个用于<bar>元素缺少外部容器元素的“替代”版本:

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);
    }
}

By using two different serializers, one for each possible XML format, you can avoid making any changes at all to your legacy XmlModel type.通过使用两种不同的序列化程序,一种用于每种可能的 XML 格式,您可以完全避免对旧的XmlModel类型进行任何更改。

Then, to deserialize flattened XML of the form然后,反序列化表单的扁平化 XML

<root>
    <bar>1</bar>
    <bar>2</bar>
</root>

You would simply do:你会简单地做:

var dS = XmlModelSerializer<XmlModel>.AlternateSerializerInstance;
using (var reader = new StringReader(xml))
{
    return (XmlModel) dS.Deserialize(reader);
}

Sample fiddle showing deserialization in both formats.示例小提琴显示两种格式的反序列化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM