2

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

我可以让序列化工作:

<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 子类创建一个自定义序列化程序来解决这个问题,或者我是否缺少其他方法来获取反序列化时的空元素?如果自定义序列化程序,有什么最好的方法/代码?

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

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

1 回答 1

2

您的问题是因为在反序列化期间 XmlSerializer 首先通过调用默认构造函数创建一个对象,然后调用ReadXml方法设置属性值,因此ReadXml无法取消对象创建。是否需要序列化 ​​null 值才能在 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

我想这就是你想要的。

于 2013-10-17T07:10:09.690 回答