【问题标题】:Deserialize XML with multiple elements into single C# object将具有多个元素的 XML 反序列化为单个 C# 对象
【发布时间】:2014-11-07 15:44:00
【问题描述】:

我需要反序列化一个如下所示的 XML 文档:

<Root>
  <Items>
    <Item>
      <ItemHeader Attr1="A" Attr2="B" Attr3="C" />
      <ItemDetails Attr4="D" Attr5="E" />
    </Item>
    ...
  </Items>
</Root>

进入一个看起来像这样的类:

[Serializable, XmlRoot("Item")]
Public class MyItem
{
  [XmlAttribute("Attr1")]
  public string Attr1 { get; set; }
  [XmlAttribute("Attr5")]
  public string Attr5 { get; set; }
}

我正在使用以下代码来执行反序列化:

XDocument doc;
XElement rootElem = doc.Element("Root");

foreach (XElement xe in rootElem.Descendants("Item"))
{
  MyItem item = new MyItem();
  XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyItem));
  XmlReader xRdr = xe.CreateReader();
  item = (MyItem)xmlSerializer.Deserialize(xRdr);
}

但是,没有任何元素被复制到对象实例中。

这可行吗?我需要反序列化每个子元素吗?

谢谢

【问题讨论】:

    标签: c# xml serialization


    【解决方案1】:

    我不确定有没有办法通过属性使用默认的 XML 序列化程序来做到这一点,而无需执行整个类结构来匹配您的 XML - 所以 ItemHeaderItemDetails 需要它们自己的类。

    您可以实现IXmlSerializable 接口,因此您可以完全自定义 - 如果您必须保持 MyItem 的结构不变。

    static void Main(string[] args)
    {
        XmlSerializer myItemSerializer = new XmlSerializer(typeof(MyItem));
    
        var xmlDoc = XDocument.Parse(@"<Item>
                                    <ItemHeader Attr1=""A"" Attr2=""B"" Attr3=""C"" />
                                    <ItemDetails Attr4=""D"" Attr5=""E"" />
                                </Item>");
    
        using (var reader = xmlDoc.CreateReader())
        {
            MyItem myItem = (MyItem)myItemSerializer.Deserialize(reader);
        }
    
    
        Console.Read();
    }
    
    [Serializable, XmlRoot("Item")]
    public class MyItem : IXmlSerializable
    {
        [XmlAttribute("Attr1")]
        public string Attr1 { get; set; }
        [XmlAttribute("Attr5")]
        public string Attr5 { get; set; }
    
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }
    
        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.ReadStartElement("Item");
    
            do
            {
                switch (reader.Name)
                {
                    case "ItemHeader":
                        Attr1 = reader.GetAttribute("Attr1");
                        reader.Read();
                        break;
                    case "ItemDetails":
                        Attr5 = reader.GetAttribute("Attr5");
                        reader.Read();
                        break;
                    default:
                        throw new XmlException(String.Format("{0} was not expected", reader.Name));
                }
            } while (reader.Name != "Item");
    
    
            reader.ReadEndElement();
        }
    
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("ItemHeader");
            writer.WriteAttributeString("Attr1", Attr1);
            writer.WriteEndElement();
            writer.WriteStartElement("ItemDetails");
            writer.WriteAttributeString("Attr5", Attr5);
            writer.WriteEndElement();
        }
    }
    

    【讨论】:

    • 这里很奇怪,我看到 Attr1 设置正确,并且仍然存在于 Main 内部,但不是 Attr5。通过调试,正在读取和设置 Attr5,但不会在 MyItem 类范围之外保留其值。
    • 上面的评论并不完全正确 :) 我的真实例子稍微复杂一些,虽然次要元素的属性在被读取时并没有“粘贴”。跨度>
    • 抱歉,我无法使用您发布的原始 XML 或我上面的内容来重现。注意 GetAttribute 如果没有找到也不会失败,它只会返回 null - 它也区分大小写。如果不是我需要你拥有的 XML。
    • 谜团解开了。我在 ItemDetails 中嵌套了另一个 Element,显然当读者点击 ItemDetails 的关闭时,读者只是将其视为 ItemDetails,并且由于没有附加到关闭的属性,因此这些属性被分配了空值。
    • 啊。我猜 reader.ReadOuterXml() 而不是 reader.Read() 可能是避免这种情况的更好/更安全的选择。
    【解决方案2】:

    问题是您收到的 xml 数据与MyItem 的序列化属性不匹配,架构不同。有多种方法可以解决这个问题,但我想最快和最肮脏的解决方案是提取您感兴趣的部分:

      XDocument doc = XDocument.Load (@"Your.xml"); 
      XElement rootElem = doc.Element("Root");
      XElement itemsElem = rootElem.Element("Items");
      foreach (XElement xe in itemsElem.Elements("Item"))
      {
        MyItem item = new MyItem()
        {
          Attr1 = xe.Element("ItemHeader").Attribute("Attr1").Value,
          Attr5 = xe.Element("ItemDetails").Attribute("Attr5").Value
        }; 
      }
    

    【讨论】:

    • 感谢您的回复,尽管可能的解决方案我将使用 Steve 的上述解决方案,因为我可以让类和属性的内部知识远离处理代码。
    猜你喜欢
    • 1970-01-01
    • 2021-03-16
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 2021-05-03
    • 2021-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多