使用 System.XML 反序列化无效的 XML

Deserializing Invalid XML with System.XML

我正在尝试反序列化一些我不负责生成的 XML。它有一个整体节点和多个模块的各种分支。问题是每个模块可能有相似的子节点,这些子节点具有不同的节点和属性但共享相同的名称。这些类似的节点没有命名空间。抽象地说,它看起来像这样作为目标类型。

<Root>
    <Module1>
         <Node SomeAttribute="123" />
    </Module1>
    <Module2>
         <Node SomeOtherAttribute="Something" />
    </Module2>
</root>

我似乎有各种建议用命名空间注释我的 pocos,以避免在我尝试使用同时具有 Module1Root 类型构造 XmlSerializer 时出现异常Module2 作为成员。

System.InvalidOperationException : Types 'Root.Module1.Item1' and 'Root.Module1.Item2' both use the XML type name, 'Item', from namespace ''. Use XML attributes to specify a unique XML name and/or namespace for the type.

我认为如果使用 System.Text.Json 我不会有这个问题,因为类型是由 poco class 结构决定的,而不是我正在反序列化的节点的名称。

有没有办法以整体形式反序列化此对象,也许可以通过使用装饰器注释 Module1.NodeModule1.Node poco class?

我试了没找到相关的装饰器。我确实成功地停止了 XmlSerializer 构造函数异常,但它停止识别 Node 类型并且无法反序列化。

我的下一步是为每个模块制作单独的 XmlSerializer 实例,并尝试看看我是否可以取消感觉效率低下的 Root 对象。

这是 fiddle 中的设置示例: https://dotnetfiddle.net/0twN0O

我有一个解决方案给你,但只有当你在使用它之前修复你的 XML 时它才会起作用(例如 123 应该与“123”一起使用)。

public class Node
{
    [XmlAttribute]
    public string SomeOtherAttribute { get; set; }

    [XmlAttribute]
    public int SomeAttribute { get; set; }
}

public class Module
{
    public Node Node { get; set; }
}

[XmlRoot("Root")]
public class OrderedItem
{
    [XmlElement("Module1")]
    public Module Module1 { get; set; }
    [XmlElement("Module2")]
    public Module Module2 { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        string xml = @"<Root>
                        <Module1>
                             <Node SomeAttribute = ""123"" /> 
                         </Module1> 
                         <Module2>
                              <Node SomeOtherAttribute = ""Something"" /> 
                          </Module2 >
                      </Root>";

        XmlSerializer serializer = new XmlSerializer(typeof(OrderedItem));
        using (TextReader reader = new StringReader(xml))
        {
            var result = (OrderedItem)serializer.Deserialize(reader);
        }

    }
}

这是对 @d-a 的回答的一个小扩展,它使用接口来帮助将对象与消费者的观点分开。

以下问题的答案非常有用: XML serialization of interface property

我不喜欢具体类型的 public 方法,但我努力使用 ISerializable 接口进行私有化和反序列化。

可能仍然会尝试使用另一个 Serialiser 来查看它的运行情况 https://github.com/ExtendedXmlSerializer/home

using System.IO;
using System.Xml;
using System.Xml.Serialization;

[XmlType]
public class Node : Module1.Node1, Module2.Node2
{
    [XmlAttribute]
    public string SomeOtherAttribute { get; set; }

    [XmlAttribute]
    public int SomeAttribute { get; set; }
}

public class Module1
{   [XmlElement(ElementName="Node")]
    public Node _node { get; set; }
    [XmlIgnore]
    public Node1 Node { get {return (Node1)_node ;} }
    
    public interface Node1 {
        public int SomeAttribute { get; set; }
    }

}

public class Module2
{   [XmlElement(ElementName="Node")]
    public Node _node { get; set; }
    [XmlIgnore]
    public Node2 Node { get {return (Node2)_node ;} }
    
    public interface Node2 {
        public string SomeOtherAttribute { get; set; }
    }
}

[XmlRoot("Root")]
public class OrderedItem
{
    [XmlElement("Module1")]
    public Module1 Module1 { get; set; }
    [XmlElement("Module2")]
    public Module2 Module2 { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        string xml = @"<Root>
                        <Module1>
                             <Node SomeAttribute = ""123"" /> 
                         </Module1> 
                         <Module2>
                              <Node SomeOtherAttribute = ""Something"" /> 
                          </Module2 >
                      </Root>";

        XmlSerializer serializer = new XmlSerializer(typeof(OrderedItem));
        using (TextReader reader = new StringReader(xml))
        {
            var result = (OrderedItem)serializer.Deserialize(reader);
            System.Console.Out.WriteLine(result.Module1.Node.SomeAttribute);
            System.Console.Out.WriteLine(result.Module2.Node.SomeOtherAttribute);
        }

    }
}

https://dotnetfiddle.net/3AYdVT

只要您使 poco 类 名称唯一即可。 属性 名称无需唯一。因此,Node的类型应该是唯一的,但这种唯一类型的成员可能都称为Node。

https://dotnetfiddle.net/0twN0O

using System.IO;
using System.Xml;
using System.Xml.Serialization;

public class Module1{
    public Node1 Node { get; set; }
    public class Node1 {
        [XmlAttribute]
        public int SomeAttribute { get; set; }
    }
}

public class Module2
{   
    public Node2 Node { get; set; }
    public class Node2 {
        [XmlAttribute]
        public string SomeOtherAttribute { get; set; }
    }
}

[XmlRoot("Root")]
public class OrderedItem
{
    [XmlElement("Module1")]
    public Module1 Module1 { get; set; }
    [XmlElement("Module2")]
    public Module2 Module2 { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        string xml = @"<Root>
                        <Module1>
                             <Node SomeAttribute = ""1232"" /> 
                         </Module1> 
                         <Module2>
                              <Node SomeOtherAttribute = ""Something"" /> 
                          </Module2 >
                      </Root>";

        XmlSerializer serializer = new XmlSerializer(typeof(OrderedItem));
        using (TextReader reader = new StringReader(xml))
        {   
            var result = (OrderedItem)serializer.Deserialize(reader);
            System.Console.Out.WriteLine(result.Module1.Node.SomeAttribute);
            System.Console.Out.WriteLine(result.Module2.Node.SomeOtherAttribute);
        }

    }
}