使用C#允许XML序列化中出现重复的节点名称

5
我正在尝试使用C#序列化一个具有重复节点名称的对象,我需要这样做的原因是因为我正在构建一个使用第三方API的库。
我需要构建的请求如下所示。
<DATASET>
   <SITE_ID>123</SITE_ID>
   <DATA type=“name”>Secondary List</DATA>
   <DATA type="extra" id="CLICKTHRU_URL">http://my.domain.com/</DATA>
   <DATA type="extra" id="REPLY_FORWARD_EMAIL">support@my.domain.com</DATA>
   <DATA type="extra" id="REPLY_FROM_EMAIL">forward@my.domain.com</DATA>
   <DATA type="extra" id="REPLY_FROM_NAME">8@yahoo.com</DATA>
   <DATA type="extra" id="REPLY_FORWARD_SUBJECT">Customer Replies</DATA>
   <DATA type="extra" id="HANDLE_UNSUBSCRIBE"></DATA>
   <DATA type="extra" id="HANDLE_AUTOREPLY"></DATA>
   <DATA type="extra" id="FOOTER_TEXT">Confidentiality agreement…</DATA>
   <DATA type="extra" id="FOOTER_HTML"> Confidentiality agreement…</DATA>
</DATASET>

我的方法是创建一个代表请求的类,并使用XML序列化属性,该类看起来像这样:

[XmlRoot("DataSet")]
public class AddListCallHolder : BaseCallHolder
{
    private BaseAttributeHolder _name = new BaseAttributeHolder(type: "");

    [XmlElement("DATA")]
    public BaseAttributeHolder Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private BaseAttributeHolder _clickthruUrl = new BaseAttributeHolder(id: "CLICKTHRU_URL");

    [XmlElement("DATA")]
    public BaseAttributeHolder CLICKTHRU_URL
    {
        get { return _clickthruUrl; }
        set { _clickthruUrl = value; }
    }
}

属性的基类是:
public class BaseAttributeHolder
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("id")]
    public string Id { get; set; }

    [XmlText]
    public string Value { get; set; }

    public BaseAttributeHolder(string value, string id, string type = "extra")
    {
        Type = type;
        Value = value;
        Id = id;
    }

    public BaseAttributeHolder(string id, string type = "extra")
    {
        Type = type;
        Id = id;
    }

    public BaseAttributeHolder(string type = "extra")
    {
        Type = type;
    }

    public BaseAttributeHolder()
    {

    }
}

当我尝试序列化一个对象时,出现了以下错误:

XML元素'DATA'来自命名空间''已经存在于当前范围内。使用XML属性为元素指定另一个XML名称或命名空间。

是否有任何解决方法可以序列化此对象或获取请求的结构。

对于一个非常好的初次提问,点赞+1。 - Jon Egerton
3个回答

2
使用数组或列表对所有的<DATA>进行反序列化,然后添加操作该数组的属性,如何?
  [XmlRoot("DataSet")]
  public class AddListCallHolder
  {
      [XmlArrayItem(typeof(BaseAttributeHolder), ElementName = "DATA")]
      public BaseAttributeHolder[] data 
      { 
           get;
           set;
      }

      [XmlIgnore]
      public BaseAttributeHolder Name
      {
          get
          {
             return data.FirstOrDefault(d => d.Type == "name");
          }
      }

      [XmlIgnore]
      public BaseAttributeHolder CLICKTHRU_URL
      {
          get
          {
             return data.FirstOrDefault(d => d.Type == "extra" && d.Id == "CLICKTHRU_URL");
          }
      }
  }

我认为你也能想出setter方法。

谢谢,干净的解决方案。 感谢大家的快速回答和解释。 - dnlgmzddr

0

如果不自定义序列化,你无法真正做到这一点;问题在于序列化程序需要在输出中唯一标识序列化属性。如果它们都有名称“DATA”,那么它就无法实现。

相反,你可以这样做:

[XmlType(TypeName="Data")]
public class BaseAttributeHolder
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("id")]
    public string Id { get; set; }

    [XmlText]
    public string Value { get; set; }


    public BaseAttributeHolder(string value, string id, string type = "extra")
    {
        Type = type;
        Value = value;
        Id = id;
    }

    public BaseAttributeHolder(string id, string type = "extra")
    {
        Type = type;
        Id = id;
    }

    public BaseAttributeHolder(string type = "extra")
    {
        Type = type;
    }

    public BaseAttributeHolder()
    {

    }
}

[XmlRoot("DataSet")]
public class AddListCallHolder
{
    [XmlElement("SITE_ID")]  
    public string Site ="";  

    private BaseAttributeHolder _name = new BaseAttributeHolder(type: "");

    [XmlElement("DATA")]
    public List<BaseAttributeHolder> Attributes { get; set; }


    public AddListCallHolder()
    {
        Attributes = new List<BaseAttributeHolder>();
        Attributes.Add(new BaseAttributeHolder(id: "CLICKTHRU_URL"));
        Attributes.Add(new BaseAttributeHolder());

    }


}

因此,您的AddCallHolder类将把其属性公开为属性列表。

如果您随后对上述内容进行序列化:

    AddListCallHolder callHolder = new AddListCallHolder();

    XmlSerializer ser = new XmlSerializer(typeof(AddListCallHolder));

    StringBuilder sb = new StringBuilder();

    TextWriter writer = new StringWriter(sb);

    ser.Serialize(writer, callHolder);

    sb.ToString();

你将获得:

    <?xml version="1.0" encoding="utf-16"?>
    <DataSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <SITE_ID />
            <Data type="extra" id="CLICKTHRU_URL" />
            <Data />
     </DataSet>

这是唯一的方法(除非编写自己的序列化方法,这很好,但显然更费力)。

编辑:

我实际上喜欢Krizz的解决方案,因为他保留了您类的原始属性,使用数组作为序列化的容器,并使用XmlIgnore属性使序列化程序在序列化/反序列化时忽略属性。属性只成为数组上的查找/插入。


0
[XmlRoot("DATASET")]
public class DS
{
    [XmlElement("SITE_ID")]
    public string Site ="";

    [XmlElement("DATA")]
    public Data[] Data = null;

}

[XmlRoot("DATA")]
public class Data
{
    [XmlAttribute("type")]
    public string Type ="";

    [XmlAttribute("id")]
    public string Id = null;

    [XmlText]
    public string Text = "";
}

XmlSerializer xs = new XmlSerializer(typeof(DS));
var obj = xs.Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(xmlstring)));

MemoryStream m = new MemoryStream();
xs.Serialize(m,obj);
MessageBox.Show(Encoding.UTF8.GetString(m.ToArray()));

这是您的 XML 字符串

string xmlstring = 
    @"
<DATASET>
   <SITE_ID>123</SITE_ID>
   <DATA type=""name"">Secondary List</DATA>
   <DATA type=""extra"" id=""CLICKTHRU_URL"">http://my.domain.com/</DATA>
   <DATA type=""extra"" id=""REPLY_FORWARD_EMAIL"">support@my.domain.com</DATA>
   <DATA type=""extra"" id=""REPLY_FROM_EMAIL"">forward@my.domain.com</DATA>
   <DATA type=""extra"" id=""REPLY_FROM_NAME"">8@yahoo.com</DATA>
   <DATA type=""extra"" id=""REPLY_FORWARD_SUBJECT"">Customer Replies</DATA>
   <DATA type=""extra"" id=""HANDLE_UNSUBSCRIBE""></DATA>
   <DATA type=""extra"" id=""HANDLE_AUTOREPLY""></DATA>
   <DATA type=""extra"" id=""FOOTER_TEXT"">Confidentiality agreement…</DATA>
   <DATA type=""extra"" id=""FOOTER_HTML""> Confidentiality agreement…</DATA>
</DATASET>
";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接