序列化/反序列化 XML 来自 Treenode

Serialize / Deserialize XML Derived From Treenode

我有两个 类、"company" 来自 Treenode 和 "document"。

[Serializable]
[XmlRoot("Company")]
public class Company : TreeNode, IXmlSerializable
{
    private string _x;
    private string _y;

    public Company() { }

    [XmlElement("X")]
    public string X { get; set; }
    [XmlElement("Y")]
    public string Y { get; set; }


    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }
    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "Company")
        {
            x = reader["X"].ToString;
            y = reader["Y"].ToString;           
        }
    }
    public void WriteXml(XmlWriter writer)
    {
        writer.WriteElementString("X", this.X.ToString());
        writer.WriteElementString("Y", this.Y.ToString());
    }
}

public class Document
{
    private int _id;
    private string _name;
    private Company _company;

    public Document() { }

    [XmlElement("ID")]
    public int ID { get; set; }
    [XmlElement("Name")]
    public string Name { get; set; }
    [XmlElement("Company")]
    public Company Comp { get; set; }
}

当我尝试序列化文档,然后保存到文件时,它起作用了。但是当我反序列化时,reader 参数总是空的。有什么解决办法吗?

这是我的反序列化代码。 "sr" 是保存 xml 文本的变量。

var sr = new StreamReader(ms);
var myStr = sr.ReadToEnd();

XmlSerializer serializer = new XmlSerializer(typeof(List<Document>));
using (TextReader tr = new StringReader(myStr))
{
    List<Document> docu = (List<Document>)serializer.Deserialize(tr);
}

我尝试实现 ISerialization 和调试但从未触发,并尝试覆盖序列化和反序列化方法,但没有成功。

我正在使用 .NET Framework 3.5

this article 中所述,正确实施 IXmlSerializable 实际上非常棘手。您对 ReadXml() 的实现似乎违反了它使用包装元素本身以及所有内容的要求,如下所示:

public void ReadXml(System.Xml.XmlReader reader)
{
    reader.MoveToContent();
    // Read attributes
    Boolean isEmptyElement = reader.IsEmptyElement; // (1)
    reader.ReadStartElement();
    if (!isEmptyElement) // (1)
    {
        // Read Child elements X and Y

        // Consume the end of the wrapper element
        reader.ReadEndElement();
    }
}

此外,reader["X"] returns 名为 "X"XML 属性 的值,如 docs。在您的 WriteXml() 中,您将 XY 的值写为嵌套的 XML 元素 。这就解释了NullReferenceException。您需要修复您的读取和写入方法以保持一致。

但是,我建议采用替代实施 IXmlSerializable 的方法,即为您的 Company 引入 代理类型 。首先,extractCompany的所有非TreeNode属性放到一个接口中:

public interface ICompany
{
    string X { get; set; }
    string Y { get; set; }
}

public class Company : TreeNode, ICompany
{
    public Company() { }

    public string X { get; set; }
    public string Y { get; set; }
}

这是可选的,但可以使代码更清晰。接下来介绍一个实现相同接口但不继承自TreeNode:

的代理POCO
public class CompanySurrogate : ICompany
{
    public string X { get; set; }
    public string Y { get; set; }

    public static implicit operator CompanySurrogate(Company company)
    {
        if (company == null)
            return null;
        // For more complex types, use AutoMapper
        return new CompanySurrogate { X = company.X, Y = company.Y };
    }

    public static implicit operator Company(CompanySurrogate surrogate)
    {
        if (surrogate == null)
            return null;
        // For more complex types, use AutoMapper
        return new Company { X = surrogate.X, Y = surrogate.Y };
    }
}

注意到代理项可以隐式转换为您的原始 Company 类型吗?现在,您可以通过将 XmlElementAttribute.Type 属性 属性 设置为代理项的属性来在 XML 序列化中使用代理项:

public class Document
{
    public Document() { }

    [XmlElement("ID")]
    public int ID { get; set; }
    [XmlElement("Name")]
    public string Name { get; set; }

    [XmlElement("Company", Type = typeof(CompanySurrogate))]
    public Company Comp { get; set; }
}

这避免了实施 IXmlSerializable 时可能出现的所有错误。给定以下输入列表:

var list = new List<Document>
{
    new Document { Name = "my name", ID = 101, Comp = new Company { X = "foo", Y = "bar", NodeFont = new System.Drawing.Font("Arial", 10) } },
    new Document { Name = "2nd name", ID = 222, Comp = new Company { X = "tlon", Y = "ukbar" } },
};

会生成如下XML罐,可以反序列化成功:

<ArrayOfDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Document>
        <ID>101</ID>
        <Name>my name</Name>
        <Company>
            <X>foo</X>
            <Y>bar</Y>
        </Company>
    </Document>
    <Document>
        <ID>222</ID>
        <Name>2nd name</Name>
        <Company>
            <X>tlon</X>
            <Y>ukbar</Y>
        </Company>
    </Document>
</ArrayOfDocument>

话虽如此,我真的不推荐这种设计。您的 UI 应该 呈现 您的数据模型,它不应该 您的数据模型。例如参见 [​​=40=]。尽可能将 Company 替换为 ICompany 可能是更改设计的第一步;您可能会发现用 TreeNode 替换现有架构变得更容易,如下所示:

public class CompanyNode : TreeNode
{
    public ICompany Company { get; set; }
}