简体   繁体   中英

XmlSerializer does not deserialize a List<string>

The class I'm trying to serialize and deserialize (roughly):

class MyDto
{
    public string Name { get; set; }

    private List<string> _uniqueNameStrings = new List<string>();

    [XmlArray("UniqueNames")]
    [XmlArrayItem("string")]
    public List<string> UniqueNameStrings 
    { 
       get => _uniqueNameStrings; 
       set => _uniqueNameStrings = value ?? new List<string>(); 
    }
}    

Previous version without null -checking, not working either:

class MyDto
{
    public string Name { get; set; }

    [XmlArray("UniqueNames")]
    [XmlArrayItem("string")]
    public List<string> UniqueNameStrings { get; set; }
}

Schema generated for this class:

<xs:complexType name="MyDto">
    <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="1" name="Name" nillable="true" type="xs:string" />
        <xs:element minOccurs="0" maxOccurs="1" name="UniqueNames" type="ArrayOfString" />
    </xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfString">
    <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="xs:string" />
    </xs:sequence>
</xs:complexType>

And the effect of serialization:

<MyDto>
    <Name>MyDtoName</Name>
    <UniqueNames>
        <string>SomeName</string>
        <string>SomeOtherName</string>
        <!-- ... <-->
    </UniqueNames>
</MyDto>

So serialization looks to be working properly, the problem is that when deserializing, XML seems to ignore the string values altogether. This is the calling code:

using System.IO.Abstractions;

private IFileSystem _fileSystem;

public void SerializeDto(object dto, string filePath)
{
    var serializer = new XmlSerializer(dto.GetType());

    using (var streamWriter = _fileSystem.FileStream.Create(filePath, FileMode.Create, FileAccess.Write))
    {
        serializer.Serialize(streamWriter, dto);
        streamWriter.Flush();
    }
}

public T DeserializeDto<T>(string filePath) where T : class
{
    var serializer = new XmlSerializer(typeof(T));

    using (var streamReader = _fileSystem.FileStream.Create(filePath, FileMode.Open, FileAccess.Read))
    {
        return (T) serializer.Deserialize(streamReader);
    }
}

The Name property (as well as all others that I have omitted for simplicity) are properly deserialized, while the UniqueNames are left empty. I've put a breakpoint at the set method and I can see that the deserializer invokes it with value equivalent to an empty List<string> , as if the <string> elements did not exist. Since I can't look into the XML code to see what's going on there, I'm stuck. Any ideas on why XmlSerializer ignores the values in the string array? Especially since it had itself serialized the values into this very format, and now can't handle reading it back...

Try this:

[XmlRoot]
public class MyDto
{
    [XMLElement]
    public string Name { get; set; }

    [XmlArray("UniqueNames"), XmlArrayItem("UniqueName")]
    public List<string> UniqueNameStrings { get; set; }

    public MyDto() { }
}

I can't tell from the example code, but it may be a missing empty class constructor. Also I updated the XmlArrayItem thinking there might be a clash with calling it 'string'.

Give this Serializer a shot:

public static void Serialize<T>(T t, string filePath, bool overwrite = true)
{
    using (var fs = new FileStream(filePath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.Write))
    {
        var serializer = new XmlSerializer(typeof(T));
        var ns = new XmlSerializerNamespaces();
        ns.Add("", ""); // Omits xmlns:xsi/xmlns:xsd

        try
        {
            serializer.Serialize(fs, t, ns);
        }
        catch (System.Exception)
        {
            if (File.Exists(filePath))
                File.Delete(filePath);

            throw;
        }
    }
}

Example:

XML:

<Names>
    <Names>John</Names>
    <Names>Doe</Names>
    <Names>Martin</Names>
    <Names>Looter</Names>
</Names>

C#:

public class Configuration
{
    [XmlArray("Names")]
    [XmlArrayItem("Names")]
    public List<string> Names { get; set; }

    public Load(Stream stream) 
    {
        var xmlSerializer = new XmlSerializer(typeof(Configuration));
        var config = (Configuration)xmlSerializer.Deserialize(stream);
        return config;
    }

}

use config.Names

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM