【问题标题】:c# Deserialize with IXmlSerializablec# 使用 IXmlSerializable 反序列化
【发布时间】:2015-10-15 16:54:47
【问题描述】:

我有这样的xml:

    <data>    
  <audit_values>
    <audit_value>
      <channel>2</channel>
      <week>
        <mo_th>6,501698000000</mo_th>
        <fr>8,414278000000</fr>
        <sa>9,292674000000</sa>
        <sun>8,551982000000</sun>
        <holid>7,164605000000</holid>
      </week>
    </audit_value>
    <audit_value>
      <channel>1</channel>
      <week>
        <mo_th>6,501698000000</mo_th>
        <fr>8,414278000000</fr>
        <sa>9,292674000000</sa>
        <sun>8,551982000000</sun>
        <holid>7,164605000000</holid>
      </week>
    </audit_value>
  </audit_values>
</data>

我需要将其反序列化为类。 但问题是,那周将来会发生变化(它将包含更多元素,它们的名称我不知道)

数据:

[XmlRoot("data")]
public class Data
{
    [XmlArray("audit_values")]
    [XmlArrayItem("audit_value", IsNullable = true)]
    public AuditValue[] AuditValues { get; set; }
}

审计值:

[XmlRoot("audit_value")]
public class AuditValue
{
    [XmlElement("week", typeof(TVR))]
    public Week Week;
}

周:

    [XmlRoot("week")]
public class Week : IXmlSerializable
{
    public Dictionary<string, double> Values = new Dictionary<string, double>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        reader.Read();
        var sub = reader.ReadSubtree();
        do
        {
            if (sub.NodeType == XmlNodeType.Element)
            {
                string name = sub.Name;
                string val = sub.ReadElementContentAsString();
                Values.Add(name, Convert.ToDouble(val));
            }
        } while (sub.Read());
    }

    public void WriteXml(XmlWriter writer)
    {

    }
}

但是在反序列化之后,我只有一个元素,并且在字典值中只记录了一个元素。我做错了什么?

GitHub: https://github.com/olegletynain/XmlTest/tree/master/XmlTestRead

【问题讨论】:

标签: c# xml deserialization xml-deserialization ixmlserializable


【解决方案1】:

我根据 @Matthew Whited 在 cmets 部分的想法调整了您的 ReadXml 方法,以下方法可以完成这项工作:

public void ReadXml(XmlReader reader)
{
    Values = XElement.Parse(reader.ReadOuterXml())
        .Elements()
        .ToDictionary(k => k.Name.ToString(), v => double.Parse(v.Value));
}

附带说明,您只需要实际根元素上的 XmlRoot,而不是每个类上的 XmlRoot,因此我将其从 AuditValue 和 Week 中删除。我也不知道TVR是什么。它没有用“typeof(TVR)”编译,所以我也把它删除了。

为了完整起见,这是我的课程版本:

[XmlRoot("data")]
public class Data
{
    [XmlArray("audit_values")]
    [XmlArrayItem("audit_value", IsNullable = true)]
    public AuditValue[] AuditValues { get; set; }
}

public class AuditValue
{
    [XmlElement("week")]
    public Week Week;
}

public class Week : IXmlSerializable
{
    public Dictionary<string, double> Values = new Dictionary<string, double>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        Values = XElement.Parse(reader.ReadOuterXml())
            .Elements()
            .ToDictionary(k => k.Name.ToString(), v => double.Parse(v.Value));
    }

    public void WriteXml(XmlWriter writer)
    {

    }
}

【讨论】:

  • 谢谢,今晚是我自己制作的方法,没有使用XElement和Linq
【解决方案2】:

您应该考虑使用 DataContractSerializer 而不是 XmlSerializer 并在 DataContract 类上实现 IExtensibleDataObject 接口。

实现 IExtensibleDataObject 允许 DataContract 类在 ExtensionData 字段中持久保存未知信息,这样可以防止在被反序列化的 XML 包含未知元素时丢失,然后重新序列化并保存。

您的 Audit 类将如下所示:

[DataContract(Name = "audit_value", Namespace = "")]
public class AuditValue : IExtensibleDataObject
{
    [DataMember(Name = "channel")]
    public int Channel { get; set; }

    [DataMember(Name = "week")]
    public Week Week { get; set; }

    public ExtensionDataObject ExtensionData { get; set; }
}

[DataContract(Name = "week", Namespace = "")]
public class Week : IExtensibleDataObject
{
    [DataMember(Name = "mo_th")]
    public Decimal MondayThroughThursday { get; set; }

    public ExtensionDataObject ExtensionData { get; set; }
}

您仍然需要更新代码以将附加元素反序列化为 POCO,但至少底层数据将被保留,直到您处理它为止。

【讨论】:

    【解决方案3】:

    试试这个。 XmlElement 消除了您没有的一系列标签。值末尾有一个额外的 's'。

    From 
        [XmlArray("audit_values")]
        [XmlArrayItem("audit_value", IsNullable = true)]
        public AuditValue[] AuditValues { get; set; }
    ​To
        [XmlElement("audit_value")]
        public AuditValue[] AuditValues { get; set; }
    

    【讨论】:

    • 您不需要两个级别的标签 只需 。使用 XlEement 代替 XmlArray 可以去掉不必要的标签。
    • 我说的是对的。您可能需要添加额外级别的标签,但不需要额外的标签。
    【解决方案4】:

    错误出现在 ReadXml 方法中,现在我将其更改为:

        public void ReadXml(XmlReader reader)
        {
            reader.Read();
            do
            {
                if (!reader.IsEmptyElement)
                {
                    var name = reader.Name;
                    var val = Convert.ToDouble(reader.ReadElementContentAsString());
                    Values.Add(name, val);
                }
                else
                {
                    reader.Skip();
                }
            } while (reader.Name != "week");
            if (reader.NodeType == XmlNodeType.EndElement)
            {
                reader.ReadEndElement();
            }
        }
    

    而且我工作正常。此解决方案不使用 XElement 和 Linq,@Volkan Paksoy 提供了更易于理解的 XElement 方法

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      • 1970-01-01
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      相关资源
      最近更新 更多