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

时间:2017-10-09 19:38:29

标签: c# xml xmlserializer

这会将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; }
    }
}

这将获得模型:

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

我正在使用遗留代码,除了更改XmlAttributes之外,我无法对XmlModel类进行更改。问题在于:实际的Xml没有&#34; foo&#34;节点:

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

所以现在我坚持让解串器吞下这个xml并输出类型XmlModel的任务。如果没有Xslt预处理或其他更复杂的方法,这可能吗?

2 个答案:

答案 0 :(得分:1)

如果您对另一种反序列化方法持开放态度,那么这将有效。它应该与XmlSerializer一样快,如果不是更快。它只是在原始xml上打开XmlReader,移动到第一个“data”元素,将数据转储到列表中,然后填充并从中返回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类不仅仅是一个简单的内在类型,例如string,那么这显然会变得更加复杂。

答案 1 :(得分:1)

您可以使用XmlAttributeOverridesXmlModel指定备用XML属性,然后使用这些属性construct an XmlSerializer执行以下操作:

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

但请注意documentation

中的以下警告
  

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

     

XmlSerializer.XmlSerializer(类型):

     

XmlSerializer.XmlSerializer(类型,字符串)

     

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

以下静态类创建并缓存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);
    }
}

通过使用两个不同的序列化程序,每个可能的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显示两种格式的反序列化。