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

本文关键字:节点 许使用 XML 序列化 | 更新日期: 2023-09-27 18:17:40

我正在尝试使用 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 名称或命名空间。

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

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

使用数组或列表反序列化所有<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");
          }
      }
  }

我想你也能想出二传手。

[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>
";

如果不自己自定义序列化,就无法真正做到这一点; 问题是序列化程序需要唯一标识输出中的序列化属性。如果它们都有"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 属性,以便序列化程序忽略序列化/反序列化的属性。这些属性只是成为数组上的数组查找/插入。