【问题标题】:XmlSerialization with XmlAnyElement to XmlNode and Order propertyXmlSerialization 与 XmlAnyElement 到 XmlNode 和 Order 属性
【发布时间】:2017-11-22 23:30:48
【问题描述】:

我在使用 .Net XmlSerializer 时遇到问题,基本上我需要一个或多个同名元素在固定模式的其他元素之间动态序列化(和反序列化)。

例子:

<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <asd>asd</asd>
  <nnn>q</nnn>
  <nnn>w</nnn>
  <nnn>e</nnn>
  <aaa>aaa</aaa>
</A>

我真正的&lt;nnn&gt; 标签有点复杂,里面有动态标签(不仅仅是条件),但我目前正在威胁这个权利。 我确实需要使用XmlElement的“Order”参数来控制一些规则。

我无法更改 XML 布局。

可序列化类示例:

[XmlRoot]
[Serializable]
public class A
{
    [XmlElement("asd", Order=1)]
    public string asd { get; set; }

    [XmlIgnore]
    public string[] qwe { get; set; }

    [XmlAnyElement("nnn", Order=2)]
    public XmlNode[] nnn
    {
        get
        {
            if (qwe == null) return null;

            var xml = new XmlDocument();
            var nodes = new List<XmlNode>(qwe.Length);

            foreach (var q in qwe)
            {
                var nnnTag = xml.CreateNode(XmlNodeType.Element, "nnn", null);
                nnnTag.InnerText = q;
                nodes.Add(nnnTag);
            }

            return nodes.ToArray();
        }
        set
        {
            if (value == null) return;
            qwe = value.Select(tag => tag.InnerText).ToArray();
        }
    }

    [XmlElement("aaa", Order=3)]
    public string aaa { get; set; }

问题是,当不使用“Order”参数时,序列化进行得很好,但是使用该参数,XmlAnyElement 之后的元素被理解为节点数组的一部分,因此不能正确反序列化。

我的示例主程序是一个控制台应用程序,主程序如下:

static void Main(string[] args)
{
    var a = new A
    {
        aaa = "aaa",
        asd = "asd",
        qwe = new[] {"q", "w", "e"}
    };
    var s = Serialize(a);
    var ss = Deserialize<A>(s);
    var s2 = Serialize(ss);

    Console.WriteLine(s);
    Console.WriteLine(s2);

    Console.WriteLine("Equals: {0};", s == s2);

    Console.ReadKey();
}

错误的输出是:

<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <asd>asd</asd>
  <nnn>q</nnn>
  <nnn>w</nnn>
  <nnn>e</nnn>
  <aaa>aaa</aaa>
</A>
<?xml version="1.0" encoding="utf-8"?>
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <asd>asd</asd>
  <nnn>q</nnn>
  <nnn>w</nnn>
  <nnn>e</nnn>
  <nnn>aaa</nnn>
</A>
Equals: False;

为了测试,这里是我正在使用的序列化/反序列化 sn-ps:

public static string Serialize<T>(T a)
{
    var s = new XmlSerializer(typeof(T));
    using (var ms = new MemoryStream())
    {
        using (TextWriter sw = new StreamWriter(ms))
        {
            s.Serialize(sw, a);
            ms.Seek(0, 0);
            using (var sr = new StreamReader(ms))
            {
                return sr.ReadToEnd();
            }
        }
    }
}

public static T Deserialize<T>(string a)
{
    var s = new XmlSerializer(typeof(T));
    var bytes = Encoding.ASCII.GetBytes(a);
    using (var ms = new MemoryStream(bytes))
    {
        return (T) s.Deserialize(ms);
    }
}

完整的源代码: https://gist.github.com/inventti-gabriel/81054269f2e0a32d7e8d1dd44f30a97f

提前致谢。

【问题讨论】:

  • 这对我来说确实像一个错误。您能否详细说明nnn 到底是什么?因为您可以以change to this 为例,但这可能不适用于您的“真实”情况。顺便说一句,在示例中请注意,您可以简化序列化/反序列化方法以使用 StringWriter/StringReader

标签: c# .net xml serialization deserialization


【解决方案1】:

我同意@CharlesMager 的观点,这似乎是XmlSerializer 中的一个错误。

话虽如此,您可以通过引入中间包装类或结构来包含任意节点,然后修改您的代理 nnn 属性以在使用 [XmlElement("nnn", Order = 2)] 标记属性后返回这些结构的数组来解决该错误:

[XmlRoot]
[Serializable]
public class A
{
    [XmlElement("asd", Order = 1)]
    public string asd { get; set; }

    [XmlIgnore]
    public string[] qwe { get; set; }

    [XmlElement("nnn", Order = 2)]
    public XmlNodeWrapper [] nnn
    {
        get
        {
            if (qwe == null)
                return null;

            var xml = new XmlDocument();
            var nodes = new List<XmlNode>(qwe.Length);

            foreach (var q in qwe)
            {
                var nnnTag = xml.CreateNode(XmlNodeType.Element, "nnn", null);
                nnnTag.InnerText = q;
                nodes.Add(nnnTag);
            }

            return nodes.Select(n => (XmlNodeWrapper)n.ChildNodes).ToArray();
        }
        set
        {
            if (value == null)
                return;
            qwe = value.Select(tag => tag.InnerText()).ToArray();
        }
    }

    [XmlElement("aaa", Order = 3)]
    public string aaa { get; set; }
}

[XmlType(AnonymousType = true)]
public struct XmlNodeWrapper
{
    public static implicit operator XmlNodeWrapper(XmlNodeList nodes)
    {
        return new XmlNodeWrapper { Nodes = nodes == null ? null : nodes.Cast<XmlNode>().ToArray() };
    }

    public static implicit operator XmlNode[](XmlNodeWrapper wrapper)
    {
        return wrapper.Nodes;
    }

    // Marking the Nodes property with both [XmlAnyElement] and [XmlText] indicates that the node array
    // may contain mixed content (I.e. both XmlElement and XmlText objects).
    // Hat tip: https://stackoverflow.com/questions/25995609/xmlserializer-node-containing-text-xml-text 
    [XmlAnyElement]
    [XmlText]
    public XmlNode[] Nodes { get; set; }

    public string InnerText()
    {
        if (Nodes == null)
            return null;
        return String.Concat(Nodes.Select(n => n.InnerText));
    }
}

请注意,XmlNodeWrapper 中的Nodes 属性同时标有[XmlAnyElement][XmlText]here 解释了这样做的要求。

示例fiddle

【讨论】:

    【解决方案2】:

    尝试使用XmlArrayAttribute 代替XmlAnyElement,因为节点是数组

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 2011-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多