自定义 XmlSerializer 问题,列表中<> 中的空元素

本文关键字:元素 自定义 问题 列表 XmlSerializer | 更新日期: 2023-09-27 18:35:25

我有一个类,我想要一个自定义序列化程序。我有时会在列表中使用这个类,我也想序列化它。其中一些元素将为空。

我可以让序列化工作:

<MyData xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Fractions>
    <Frac>1/2</Frac>
    <Frac xsi:nil="true" />
    <Frac xsi:nil="true" />
    <Frac>3/6</Frac>
  </Fractions>
</MyData>

但是,当如上所述存在空元素时,反序列化不起作用。List<> 序列化程序似乎正在为元素调用 ReadXml(),而不是仅在列表中创建一个空元素。

当我运行我的示例时,反序列化版本是:

1/2
/
/
3/6

即在元素 1 和 2 中创建了一个 MyFrac 对象而不是 null。

我是否必须为 List 子类创建自定义序列化程序来解决此问题,或者我是否缺少其他方法来获取反序列化的 null 元素?如果是自定义序列化程序,有什么最佳方法/代码吗?

我在下面有一个完整的示例,显示了我当前的实现。

public class MyFrac : IXmlSerializable
{
    public string N;
    public string D;
    public override string ToString()
    {
        return N + "/" + D;
    }
    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
    {
        return null;
    }
    void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
    {
        if (reader.IsEmptyElement && reader.NodeType != XmlNodeType.EndElement)
        {
            reader.Read();
            return;
        }
        reader.ReadStartElement();
        string sfrac = reader.ReadString();
        try
        {
            var m = Regex.Match(sfrac, @"('d+)/('d+)");
            if (!m.Success)
                throw new Exception(sfrac + " was not in the correct format");
            N = m.Result("$1");
            D = m.Result("$2");
        }
        finally
        {
            reader.ReadEndElement();
        }
    }
    void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteString(N + "/" + D);
    }
}
public class MyData
{
    [XmlArrayItem("Frac")]
    public List<MyFrac> Fractions;
}
public static void Run()
{
    var data = new MyData();
    data.Fractions = new List<MyFrac>();
    data.Fractions.Add(new MyFrac { N = "1", D = "2" });
    data.Fractions.Add(null);
    data.Fractions.Add(null);
    data.Fractions.Add(new MyFrac { N = "3", D = "6" });
    var serializer = new XmlSerializer(typeof(MyData));
    StringBuilder sb = new StringBuilder();
    using (var writer = new StringWriter(sb))
    {
        serializer.Serialize(writer, data);
    }
    // Dump XML
    Console.WriteLine(sb.ToString());
    using (var reader = new StringReader(sb.ToString()))
    {
        var data2 = (MyData)serializer.Deserialize(reader);
        Console.WriteLine(data2.Fractions[0]);
        Console.WriteLine(data2.Fractions[1]);
        Console.WriteLine(data2.Fractions[2]);
        Console.WriteLine(data2.Fractions[3]);
    }
}

自定义 XmlSerializer 问题,列表中<> 中的空元素

由于在反序列化期间出现问题 XmlSerializer 首先通过调用默认构造函数创建一个对象,然后调用 ReadXml 方法来设置属性值,以便 ReadXml 无法取消对象创建。是否需要序列化空值才能在 xml 中查看它?我的意思是,您可以通过使用非列表集合来避免这种情况。例如,创建自定义集合:

public class MyCollection : System.Collections.ObjectModel.Collection<MyFrac>
{
    protected override void InsertItem(int index, MyFrac item)
    {
        if(item == null) return;
        base.InsertItem(index, item);
    }       
    protected override void SetItem(int index, MyFrac item)
    {
        if(item == null) 
        {
            base.RemoveAt(index);
        }
        else
        {
            base.SetItem(index, item);
        }
    }
}

MyData 类中使用它:

public class MyData
{
    [XmlArrayItem("Frac")]
    public MyCollection Fractions;
}

然后序列化/反序列化按预期工作:

    public static void Main(string[] args)
    {
        var data = new MyData();
        data.Fractions = new MyCollection();
        data.Fractions.Add(new MyFrac { N = "1", D = "2" });
        data.Fractions.Add(null);
        data.Fractions.Add(null);
        data.Fractions.Add(new MyFrac { N = "3", D = "6" });
        var serializer = new XmlSerializer(typeof(MyData));
        StringBuilder sb = new StringBuilder();
        using (var writer = new StringWriter(sb))
        {
            serializer.Serialize(writer, data);
        }
        // Dump XML
        Console.WriteLine(sb.ToString());
        using (var reader = new StringReader(sb.ToString()))
        {
            var data2 = (MyData)serializer.Deserialize(reader);
            foreach (var element in data2.Fractions) {
                Console.WriteLine(element);
            }
        }
        Console.ReadLine();
    }

序列化的 xml:

<?xml version="1.0" encoding="utf-16"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Fractions>
    <Frac>1/2</Frac>
    <Frac>3/6</Frac>
  </Fractions>
</MyData>

输出:

1/2

3/6

更新

好的,您需要一个具有自定义序列化规则的集合。让我们实现它:

public class MyCollection<T> : Collection<T>, IXmlSerializable where T: class 
{
    public XmlSchema GetSchema()
    {
        return null;
    }
    public void ReadXml(XmlReader reader)
    {
        var serializer = new XmlSerializer(typeof(T));
        var wasEmpty = reader.IsEmptyElement;
        reader.Read();
        if (wasEmpty)
            return;
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            if (reader.IsEmptyElement)
            {
                reader.Read();
                Items.Add(null);
                continue;
            }
            var item = (T)serializer.Deserialize(reader);
            Items.Add(item);
        }
        reader.ReadEndElement();
    }
    public void WriteXml(XmlWriter writer)
    {
        var serializer = new XmlSerializer(typeof (T));
        foreach (var myFrac in Items)
        {
            serializer.Serialize(writer, myFrac);
        }
    }
}

用法:

public class MyData
{
    public MyCollection<MyFrac> Fractions;
}
class Program
{
    static void Main(string[] args)
    {
        var data = new MyData();
        data.Fractions = new MyCollection<MyFrac>();
        data.Fractions.Add(new MyFrac { N = "1", D = "2" });
        data.Fractions.Add(null);
        data.Fractions.Add(null);
        data.Fractions.Add(new MyFrac { N = "3", D = "6" });
        var serializer = new XmlSerializer(typeof(MyData));
        StringBuilder sb = new StringBuilder();
        using (var writer = new StringWriter(sb))
        {
            serializer.Serialize(writer, data);
        }
        // Dump XML
        Console.WriteLine(sb.ToString());
        Trace.WriteLine(sb.ToString());
        using (var reader = new StringReader(sb.ToString()))
        {
            var data2 = (MyData)serializer.Deserialize(reader);
            foreach (var fraction in data2.Fractions)
            {
                var output = fraction == null ? "null" : fraction.ToString();
                Console.WriteLine(output);
                Trace.WriteLine(output);
            }
        }
        Console.ReadLine();
    }
}

输出 xml:

<?xml version="1.0" encoding="utf-16"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Fractions>
    <MyFrac>1/2</MyFrac>
    <MyFrac xsi:nil="true" />
    <MyFrac xsi:nil="true" />
    <MyFrac>3/6</MyFrac>
  </Fractions>
</MyData>

输出数据:

1/2

3/6

我想这就是你想要的。