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

Deserialize xml into a class with different hierarchy?

这会将 xml 样本反序列化为 "XmlModel" class。

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

这将得到模型:

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

我正在使用遗留代码,除了更改 Xml 属性外,我无法更改 Xml 模型 class。这是问题所在:实际 Xml 没有 "foo" 节点:

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

所以现在我被困在让反序列化器吞下这个 xml 和输出类型 Xml 模型的任务中。如果没有 Xslt 预处理或其他更复杂的方法,这可能吗?

如果您对反序列化的替代方法持开放态度,这会起作用。它应该和 XmlSerializer 一样快,如果不是更快的话。它只是在原始 xml 上打开一个 XmlReader,移动到第一个 "data" 元素,将数据转储到列表中,然后填充并 returns 你的 XmlModel 从中

LINQPad 文件可用 here

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

如果您的 bar class 不仅仅是一个简单的内部类型,这显然会变得更加复杂,例如 string.

您可以使用 XmlAttributeOverrides to specify alternate XML attributes for your XmlModel, then construct an XmlSerializer 通过执行以下操作来使用这些属性:

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

但是,请注意 documentation 中的以下警告:

To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:

XmlSerializer.XmlSerializer(Type)

XmlSerializer.XmlSerializer(Type, String)

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...

以下静态 class 创建并缓存 2 个序列化程序,一个用于 XmlModel 的 "current" 版本,另一个用于 "alternate" 版本,其中 <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);
    }
}

通过使用两种不同的序列化程序,一种用于每种可能的 XML 格式,您可以避免对遗留 XmlModel 类型进行任何更改。

然后,反序列化扁平化XML的形式

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

你只需要做:

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

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