将对象序列化为XML数组

问题描述

我有一种情况,可能需要将一个对象作为根元素序列化为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);