允许使用 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 名称或命名空间。
是否有任何解决方法可以序列化此对象或获取请求的结构。
使用数组或列表反序列化所有<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 属性,以便序列化程序忽略序列化/反序列化的属性。这些属性只是成为数组上的数组查找/插入。