首页 > 解决方案 > 如何从 C# 中的通用接口派生的 XML 反序列化子集合

问题描述

我正在尝试反序列化包含从通用接口派生的子项的类。这是我的界面和两个示例类:

public interface IItem<T> : IXmlSerializable
{
    T Value { get; set; }
    string Name { get; set; }
    List<IItem<T>> ChildItems { get; set; }
}

public class TypeA : IItem<string>
{
    public string Value { get; set; }
    public string Name { get; set; }

    public List<IItem<string>> ChildItems { get; set; }

    public TypeA()
    {
        ChildItems = new List<IItem<string>>();
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        var userQuery = new UserQuery();
        Func<string, string> converter = value => value;
        userQuery.ReadFromXml(reader, this, converter);
    }

    public void WriteXml(XmlWriter writer)
    {
        var userQuery = new UserQuery();
        userQuery.WriteToXml(this, writer);
    }
}

public class TypeB : IItem<int>
{
    public int Value { get; set; }
    public string Name { get; set; }

    public List<IItem<int>> ChildItems { get; set; }

    public TypeB()
    {
        ChildItems = new List<IItem<int>>();
    }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        var userQuery = new UserQuery();
        Func<string, int> converter = value => Convert.ToInt16(value);
        userQuery.ReadFromXml<int>(reader, this, converter);
    }

    public void WriteXml(XmlWriter writer)
    {
        var userQuery = new UserQuery();
        userQuery.WriteToXml(this, writer);
    }
}

我已经阅读了很多 SO 文章并得出结论,我需要使用 IXmlSerializable 接口来实现我自己的序列化。到目前为止,序列化效果很好!这是我用来序列化的方法:

public void WriteToXml<T>(IItem<T> item, XmlWriter writer)
{
    writer.WriteElementString("Value", item.Value.ToString());
    writer.WriteElementString("Name", item.Name);

    writer.WriteStartElement("ChildItems");
    foreach (var child in item.ChildItems)
    {
        // i want the name of the type to be the element name
        writer.WriteStartElement(child.GetType().Name);
        child.WriteXml(writer);
        writer.WriteEndElement();
    }
    writer.WriteEndElement();
}

我遇到的问题是反序列化子项。我反序列化孩子的方法如下所示:

public void ReadFromXml<T>(XmlReader reader, IItem<T> item, Func<string, T> parseExpectedValue)
{
    reader.MoveToContent();
    reader.ReadToFollowing("Value");
    item.Value = parseExpectedValue(reader.ReadElementString("Value"));

    reader.ReadToFollowing("Name");
    item.Name = reader.ReadElementString("Name");

    // todo: how to implement reading of child items ??
    reader.ReadToFollowing("ChildItems");
    // the statement below throws an InvalidOperationException with
    // the inner exception message: Unexpected node type Element. ReadElementString method can only be called on elements with simple or empty content

    var value = reader.ReadElementString();
}

对于序列化和反序列化,我使用以下方法:

public string Serialize<T>(T item)
{
    var type = item.GetType();
    var serializer = new XmlSerializer(type);
    var serializedItem = string.Empty;
    using (var stringWriter = new StringWriter())
    {
        serializer.Serialize(stringWriter, item);
        serializedItem = stringWriter.ToString();
    }

    return serializedItem;
}

public T Deserialize<T>(string serializedItem) where T : class
{
    T item = null;
    try
    {
        var type = typeof(T);
        var serializer = new XmlSerializer(type);
        using (var stringReader = new StringReader(serializedItem))
        {
            item = (T)serializer.Deserialize(stringReader);
        }
    }
    catch (System.Exception ex)
    {
        ex.Dump();
    }

    return item;    
}

当我序列化以下对象时,XML 结果如下所示:

var testItemA = new TypeA() 
    {
        Name = "TestTypeA",
        Value = "TestValue",
        ChildItems = new List<UserQuery.IItem<string>>()
        {
            new TypeA() { Name = "Str Child 1", Value = "Str Child Value 1", },
            new TypeA() { Name = "Str Child 2", Value = "Str Child Value 2", ChildItems = new List<UserQuery.IItem<string>>(){ new TypeA(){ Name = "Str Child 3", Value = "Test Value 3" } }},
        }
    };


    var testItemB = new TypeB()
    {
        Name = "TestTypeB",
        Value = 1234,
        ChildItems = new List<UserQuery.IItem<int>>()
        {
            new TypeB() { Name = "Child1", Value = 100, },
            new TypeB() { Name = "Child2", Value = 200, }
        }
    };

XML 输出:

<?xml version="1.0" encoding="utf-16"?>
<TypeA>
  <Value>TestValue</Value>
  <Name>TestTypeA</Name>
  <ChildItems>
    <TypeA>
      <Value>Str Child Value 1</Value>
      <Name>Str Child 1</Name>
      <ChildItems />
    </TypeA>
    <TypeA>
      <Value>Str Child Value 2</Value>
      <Name>Str Child 2</Name>
      <ChildItems>
        <TypeA>
          <Value>Test Value 3</Value>
          <Name>Str Child 3</Name>
          <ChildItems />
        </TypeA>
      </ChildItems>
    </TypeA>
  </ChildItems>
</TypeA>

<?xml version="1.0" encoding="utf-16"?>
<TypeB>
  <Value>1234</Value>
  <Name>TestTypeB</Name>
  <ChildItems>
    <TypeB>
      <Value>100</Value>
      <Name>Child1</Name>
      <ChildItems />
    </TypeB>
    <TypeB>
      <Value>200</Value>
      <Name>Child2</Name>
      <ChildItems />
    </TypeB>
  </ChildItems>
</TypeB>

我知道我必须遍历 XML 中的子元素,但是我必须使用 XmlReader 的哪些方法?

标签: c#xml

解决方案


推荐阅读