C# IXmlSerializable ReadXml实现
在执行XML反序列化时,我试图将XML标记的名称放入类属性中。我需要将名称作为属性,因为多个XML标记共享同一个类。XML和相关的类定义如下 我收到的XML响应格式如下:C# IXmlSerializable ReadXml实现,c#,.net,deserialization,xml-deserialization,ixmlserializable,C#,.net,Deserialization,Xml Deserialization,Ixmlserializable,在执行XML反序列化时,我试图将XML标记的名称放入类属性中。我需要将名称作为属性,因为多个XML标记共享同一个类。XML和相关的类定义如下 我收到的XML响应格式如下: <Data totalExecutionTime="00:00:00.0467241"> <ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127
<Data totalExecutionTime="00:00:00.0467241">
<ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127">
<Value modifiedDate="2017-03-24T12:07:12.77">ABC1234</Value>
<Category id="5432" parentID="9876" itemOrder="0" modifiedDate="2017-03-24T12:16:23.687">The best category</Category>
... <!-- like 100 other elements -->
</ItemNumber>
</Data>
和一个顶级类,ItemData
:
[Serializable]
[XmlRoot("Data")]
public class ItemData
{
[XmlAttribute("totalExecutionTime")]
public string ExecutionTime { get; set; }
[XmlElement("ItemNumber", Type = typeof(ItemBase))]
public List<ItemBase> Items { get; set; }
}
最后是ItemProperty
:
public class ItemProperty : IXmlSerializable
{
public static ItemProperty Empty { get; } = new ItemProperty();
public ItemProperty()
{
this.Name = string.Empty;
this.Value = string.Empty;
this.Id = 0;
this.Order = 0;
}
public string Name { get; set; }
[XmlText] // no effect while using IXmlSerializable
public string Value { get; set; }
[XmlAttribute("id")] // no effect while using IXmlSerializable
public int Id { get; set; }
[XmlAttribute("itemOrder")] // no effect while using IXmlSerializable
public int Order { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
string name = reader.Name;
this.Name = name;
string val = reader.ReadElementString();
this.Value = val;
if (reader.HasAttributes)
{
string id = reader.GetAttribute("id");
this.Id = Convert.ToInt32(id);
string itemOrder = reader.GetAttribute("itemOrder");
this.Order = Convert.ToInt32(itemOrder);
string sequence = reader.GetAttribute("seq");
this.Sequence = Convert.ToInt32(sequence);
}
// it seems the reader doesn't advance to the next element after reading
if (reader.NodeType == XmlNodeType.EndElement && !reader.IsEmptyElement)
{
reader.Read();
}
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}
实现IXmlSerializable
接口的意义在于,我最终需要存储为ItemProperty
的XML标记的名称,并且在使用XML类/属性时不会捕获该信息。我认为情况就是这样,因为属性决定了反序列化使用哪个类,通常每个XML标记都有一个关联的类。我不想这样做,因为响应中可能有大量不同的标记,它们都具有相似的属性。因此,ItemProperty
类
我还尝试通过ItemProperty
中的参数化构造函数传递标记的名称,并在其中设置name属性,但在执行反序列化时,它使用默认构造函数,然后设置属性值,因此这不是一个选项
反射也不起作用,因为类总是ItemProperty
,因此没有唯一的名称
也许我构造XML属性的方式可以用不同的方式来实现我想做的事情,但我看不到这一点
我对解决这个问题的任何方法都持开放态度,但我非常确定这需要实现IXmlSerializable.ReadXml()
。我知道XmlReader
的工作是阅读整个XML并将读者推进到文本末尾,但我不太清楚如何做到这一点
TLDR:如何正确实现IXmlSerializable.ReadXml()
,同时将XML标记名、标记值和所有属性捕获到类属性中?
编辑:通过更新的
ReadXml
方法,我获得了ItemProperty
级别所需的所有数据,但是classItemData
,Items
列表只有一个项。我之所以这样认为是因为我没有正确地推进读者的阅读。来自以下文档:
调用此方法时,读卡器位于包装类型信息的开始标记上。。。当此方法返回时,它必须从头到尾读取整个元素,包括其所有内容。与WriteXml
方法不同,框架不会自动处理包装器元素。您的实现必须这样做。未能遵守这些定位规则可能会导致代码生成意外的运行时异常或损坏的数据
您的ReadXml()
可以修改以满足以下要求:
XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.
if (reader.HasAttributes)
{
var id = reader.GetAttribute("id");
if (id != null)
// Since id is missing from some elements you might want to make it nullable
this.Id = XmlConvert.ToInt32(id);
var order = reader.GetAttribute("itemOrder");
if (order != null)
// Since itemOrder is missing from some elements you might want to make it nullable
this.Order = XmlConvert.ToInt32(order);
string sequence = reader.GetAttribute("seq");
//There is no Sequence property?
//this.Sequence = Convert.ToInt32(sequence);
}
// Read element value.
// This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
// thus there is no need for an additional Read()
this.Value = reader.ReadElementContentAsString();
}
注:
ReadElementString()
:
我们建议您使用该方法读取文本元素
根据建议,我修改了您的ReadXml()
以使用此方法。反过来,其文件:
此方法读取开始标记和元素的内容,并将读取器移过结束元素标记
因此,此方法应使XmlReader
完全按照ReadXml()
的要求定位,以确保读取器正确前进ItemProperty
元素的XML属性必须在读取该元素的内容之前进行处理,因为读取内容会使读取器超过元素的起始位置及其属性Name
属性中包含名称空间前缀(如果有)演示小提琴。可能相关:。这回答了你的问题吗?它显示了将与
IXmlSerializable
节点相对应的整个元素加载到XElement
中的示例。不完全是这样,因为该示例在运行前已知反序列化的元素名称。我确实获得了ItemProperty
级别所需的所有数据,但我相信读者不会进步,因为ItemData.Items
只包含一个项目。请参见底部的编辑。谢谢
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.
if (reader.HasAttributes)
{
var id = reader.GetAttribute("id");
if (id != null)
// Since id is missing from some elements you might want to make it nullable
this.Id = XmlConvert.ToInt32(id);
var order = reader.GetAttribute("itemOrder");
if (order != null)
// Since itemOrder is missing from some elements you might want to make it nullable
this.Order = XmlConvert.ToInt32(order);
string sequence = reader.GetAttribute("seq");
//There is no Sequence property?
//this.Sequence = Convert.ToInt32(sequence);
}
// Read element value.
// This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
// thus there is no need for an additional Read()
this.Value = reader.ReadElementContentAsString();
}