问题描述
我有一种情况,可能需要将一个对象作为根元素序列化为XML文件,或者如果提供的文件已经具有对象数组,则需要将该对象序列化为该数组。
因为该对象可能是根,所以我用System.Xml.Serialization.XmlRootAttribute
装饰了它。
当我将对象序列化为XElement时,我得到了对象的根版本。当我将XElement添加到数组时,它会保留名称空间属性,并且无法正确反序列化。
这是课程的一部分:
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd","4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:www.agxml.org:schemas:all:4:0",TypeName = "commodityMovement")]
[System.Xml.Serialization.XmlRootAttribute(ElementName ="commodityMovement",Namespace="urn:www.agxml.org:schemas:all:4:0",IsNullable=false)]
public partial class commodityMovementType {
...
}
当我创建带有列表的文件时,它工作得很好。
private static String CreateMessageFile<T>(String filePath,T message)
{
var xmlDoc = new XDocument();
// serialize XML to string to the xmlEntity document
using (var writer = new StringWriter())
{
var entitySerializer = new XmlSerializer(typeof(List<T>));
var list = new List<T>();
list.Add(message);
entitySerializer.Serialize(writer,list);
xmlDoc.Add(XElement.Parse(writer.ToString()));
}
xmlDoc.Save(filePath);
}
这将使用一条消息序列化一个数组:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfcommodityMovement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<commodityMovement>
...
</commodityMovement>
</ArrayOfcommodityMovement>
private static String CreateMessageFile<T>(String filePath,T message)
{
var xmlDoc = new XDocument();
using var sourceStream = File.Open(filePath,FileMode.Open);
xmlDoc.Add(XElement.Load(sourceStream));
using (var writer = new StringWriter())
{
var entitySerializer = new XmlSerializer(entity.DotNetType);
// entitySerializer.Serialize(writer,list);
entitySerializer.Serialize(writer,message);
xmlDoc.Root.Add(XElement.Parse(writer.ToString()));
}
xmlDoc.Save(filePath);
}
这将产生以下XML:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfcommodityMovement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<commodityMovement>
...
</commodityMovement>
<commodityMovement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:www.agxml.org:schemas:all:4:0">
...
</commodityMovement>
</ArrayOfcommodityMovement>
当我尝试反序列化ArrayOfcommodityMovement时,它只会反序列化第一条commodityMovement消息。
如果我打开生成的XML文件并从第二个commodityMovement元素中删除名称空间属性,则它将正确反序列化。这是我用来定义“正确”的测试。
XDocument xDocument = XDocument.Load(filePath);
var descendants = xDocument.Descendants("commodityMovement");
Assert.Equal(2,descendants.Count());
var entitySerializer = new XmlSerializer(typeof(List<commodityMovementType>));
var commodityMovementList = entitySerializer.Deserialize(xDocument.CreateReader()) as List<commodityMovementType>;
Assert.NotEmpty(commodityMovementList);
Assert.Equal(2,commodityMovementList.Count);
那么我该如何反序列化对象并将结果元素插入到现有数组中,并确保未添加属性?
顺便说一句,我需要保留System.Xml.Serialization.XmlRootAttribute,因为根据配置,我需要能够为每个文件生成一条消息,然后commodityMovement成为根元素。
在此先感谢您的帮助。
解决方法
如果这不是数组中的第一个元素,我发现解决方案是删除属性和命名空间。要查看它是否是第一个,我检查文档根目录是否为空:
if (xmlDoc.Root is null)
如果它不为空,我将元素序列化,删除它的属性和命名空间并将元素添加到根元素:
else
{
var element = SerializeEntityAsElement(entity,masterData);
// remove attributes and namespace so element can be added to existing array
element.RemoveAttributes();
element.Name = element.Name.LocalName;
xmlDoc.Root.Add(element);
}
现在我的测试将通过:
XDocument xDocument = XDocument.Load(filePath);
var descendants = xDocument.Descendants("CommodityMovement");
Assert.Equal(2,descendants.Count());
var entitySerializer = new XmlSerializer(typeof(List<CommodityMovementType>));
var commodityMovementList = entitySerializer.Deserialize(xDocument.CreateReader()) as List<CommodityMovementType>;
Assert.NotEmpty(commodityMovementList);
Assert.Equal(2,commodityMovementList.Count);