【问题标题】:Deserializing a single XML stack into multiple objects将单个 XML 堆栈反序列化为多个对象
【发布时间】:2015-03-24 15:03:35
【问题描述】:

我正在开发一个返回单个 XML 块的项目,例如:

<root>
  <x_val />
  <x_val2 />
  <x_addr1 />
  <x_addr2 />
  <x_city />
  <x_state />
  <x_country />
  <x_zip />
</root>

在这个项目中,我可以进行多次调用,返回各种信息,但大部分都会在底部包含地址信息。

在编写我的 C# 类时,我不希望必须为我创建的每个响应对象中的每个节点创建属性。相反,我更愿意定义一个 Address 对象,这样使用:

[XmlRoot("root")]
[Serializable]
public class ReturnItem1
{
    [XmlElement("x_val")]
    public string FirstValue{ get; set; }

    [XmlElement("x_val2")]
    public string SecondValue{ get; set; }

    public Address AddressInfo { get; set; }

    public ReturnItem1()
    {
        AddressInfo = new Address();
    }
}

public class Address
{
    [XmlElement("x_addr1")]
    public string Address1 { get; set; }

    [XmlElement("x_addr2")]
    public string Address2 { get; set; }

    [XmlElement("x_city")]
    public string City { get; set; }

    [XmlElement("x_state")]
    public string State { get; set; }

    [XmlElement("x_country")]
    public string Country { get; set; }

    [XmlElement("x_zip")]
    public string PostalCode { get; set; }
}

反序列化时,我正在执行以下逻辑(响应类型为 XElement):

var serializer = new XmlSerializer(typeof(ReturnItem1));
var returnObject = (ReturnItem1)serializer.Deserialize(response.CreateReader());

FirstValue 和 SecondValue 的值填充在 returnObject 中,但 Address 中的属性始终为 null。

我尝试将 [XmlRoot("root)"] 属性添加到 Address 类,但没有奏效。也没有将 ReturnItem1 中 Address 属性的 XmlElement 属性设置为“root”或任何其他节点名称。

有没有办法使用这种方法将单个 XML 节点反序列化为具有一个或多个子对象的对象?

【问题讨论】:

    标签: c# xml serialization xml-deserialization


    【解决方案1】:

    尝试使用与 XML 元素匹配的私有成员和封装它们的公共地址属性。看这个例子:

    https://msdn.microsoft.com/en-us/library/6exf3h2k%28v=vs.110%29.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-3

    【讨论】:

    • 我尝试了您的建议,但从未调用过 OnDeserialized 方法。为了以防万一,我也尝试了 OnDeserializing,但没有骰子。
    • 那很奇怪,你能把完整的代码贴出来吗?
    • 我通过将 Order 属性添加到每个 DataMember 标记来使其工作,因为如果属性不是按字母顺序排列的,反序列化器显然希望这些属性存在。但是,我仍然对结果不满意(请参阅下面的帖子)。
    【解决方案2】:

    我已经设法使用 TAMTAM 的建议将 XML 反序列化为对象,如下所示:

    [DataContract(Name="root", Namespace = "")]
    public class ReturnItem1
    {
        [DataMember(Name = "x_val", Order = 0)]
        public string FirstValue { get; set; }
    
        [DataMember(Name = "x_val2", Order = 1)]
        public string SecondValue { get; set; }
    
        [DataMember(Name = "x_addr1", Order = 2)]
        private string _address;
    
        [DataMember(Name = "x_addr2", Order = 3)]
        private string _address2;
    
        [DataMember(Name = "x_city", Order = 4)]
        private string _city;
    
        [DataMember(Name = "x_state", Order = 5)]
        private string _state;
    
        [DataMember(Name = "x_country", Order = 6)]
        private string _country;
    
        [DataMember(Name = "x_zip", Order = 7)]
        private string _postalCode;
    
        public Address AddressInfo { get; set; }
    
        [OnDeserialized()]
        void OnDeserialized(StreamingContext context)
        {
            AddressInfo = new Address
            {
                Address1 = _address,
                Address2 = _address2,
                PostalCode = _postalCode,
                City = _city,
                Country = _country,
                State = _state
            };
        }
    }
    

    我终于找到了一个我可以接受的解决方案。 ReturnItem1 扩展了包含 AdditionalAttributesToDeserialize 列表的基类。然后在 ReturnItem1 的 OnDeserialized 方法中,我将“AddressInfo”添加到该列表中。在反序列化基础对象之后,我然后遍历该列表,使用反射来查找属性和属性的类型,并且程序为每个条目创建一个新的 DataContractSerializer 并在使用反射将属性的值设置为新之前反序列化属性 -反序列化的对象。这是一种蛮力的方法,在将属性名称添加到 AdditionalPropertiesToDeserialize 列表时很容易对属性名称进行粗暴的处理,但它确实允许我在一个地方定义我的对象和属性并随意重用它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-03
      • 1970-01-01
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多