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

时间:2013-10-14 23:39:36

标签: c# xml-serialization

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

我可以使序列化工作:

<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>

但是当像上面那样存在null元素时,反序列化不起作用。列表&lt;&gt; serializer似乎在调用元素的ReadXml()而不是在列表中创建一个null元素。

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

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]);
    }
}

1 个答案:

答案 0 :(得分:2)

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

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

我认为这就是你想要的。