在执行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">
<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>反序列化如下:
XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}和一个顶级的类,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; }
}ItemBase被定义为:
[Serializable]
public class ItemBase
{
[XmlElement("Value")]
public virtual ItemProperty ItemNumber { get; set; } = ItemProperty.Empty;
[XmlElement("ItemName")]
public virtual ItemProperty Category { get; set; } = ItemProperty.Empty;
... // like 100 other properties
}最后是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 /property属性时不会捕获这些信息。我相信情况是这样的,因为属性决定了用于反序列化的类,通常每个XML标记都有一个关联的类。我不想朝这个方向走,因为响应中可能有这么多不同的标记,而且它们都有相似的属性。因此,出现了ItemProperty类。
我还尝试通过ItemProperty中的参数化构造函数传递标记的名称,并在那里设置name属性,但在执行反序列化时,它使用默认构造函数,然后设置属性值,因此这不是一个选项。
反射也不起作用,因为类总是ItemProperty,因此没有唯一的名称。
也许我如何构造XML属性可以用不同的方式来实现我想要做的事情,但我没有看到。
我对任何解决这个问题的方法都是开放的,但是我很确定它需要实现IXmlSerializable.ReadXml()。我知道XmlReader的工作是读取整个XML,并将读者推进到文本的末尾,但我对如何做到这一点还不太清楚。
如何在捕获XML标记名、标记值和类属性中的所有属性时正确地实现IXmlSerializable.ReadXml() ?
编辑:通过更新的ReadXml方法,我获得了ItemProperty级别所需的所有数据,但是类ItemData,Items列表中只有一个项。我认为是因为我没有正确地推进读者。
发布于 2018-12-05 00:30:31
来自IXmlSerializable.ReadXml(XmlReader)的文档
当调用此方法时,读取器定位在开始标记上,该标记包装您的类型的信息。..。当此方法返回时,它必须从头到尾读取整个元素,包括其所有内容。与
WriteXml方法不同,框架不会自动处理包装器元素。您的实现必须这样做。不遵守这些定位规则可能会导致代码产生意外的运行时异常或损坏数据。
您的ReadXml()可以按以下方式修改以满足这些要求:
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(),其文档州:
我们建议您使用ReadElementContentAsString()方法读取文本元素。按照建议,我修改了您的ReadXml()以使用此方法。反过来,它的文档州
此方法读取开始标记、元素的内容,并将读取器移过结束元素标记。
因此,这个方法应该使XmlReader的位置与ReadXml()所要求的完全一致,以确保读取器是正确的。
ItemProperty元素的XML属性必须在读取该元素的内容之前进行处理,因为读取内容将使读取器通过元素开始-及其属性。XmlConvert类的实用程序来解析和格式化XML原语,这样数值值和日期/时间值就不会被错误地本地化。Name属性中包括名称空间前缀(如果有的话)。演示小提琴这里。
https://stackoverflow.com/questions/53527040
复制相似问题