【问题标题】:Serialize / Deserialize XML Derived From Treenode序列化/反序列化从 Treenode 派生的 XML
【发布时间】:2017-06-24 17:16:31
【问题描述】:

我有两个类,从 Treenode 派生的“公司”和“文档”。

[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; }
}

当我尝试序列化文档,然后保存到文件时,它的工作。但是当我反序列化时,读取器参数始终为空。有什么解决办法吗?

这是我的反序列化代码。 "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

【问题讨论】:

  • 您检查过生成的 XML 吗?
  • @Fildor 是的,我有支票。反序列化时,公司数据永远不会填充。
  • 尝试删除 Company 类中的 IXmlSerializable 和 GetSchema、ReadXml 和 WriteXML 方法。然后再试一次。
  • 哦,去掉 [XmlRoot("Company")] - 只有一个根!
  • 完全有可能,是的。公司是否绝对有必要实施 TreeNode?难道不能有一个单独的 TreeNode-Child ,比如说“CompanyTreeNode”,它派生自 TreeNode 并有一个 Company 作为字段?

标签: c# xml serialization deserialization treenode


【解决方案1】:

正如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"] 返回名为"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 应该呈现您的数据模型,而不应该成为您的数据模型。参见例如How does one implement UI independent applications?。尽可能将Company 替换为ICompany 可能是改变设计的第一步;您可能会发现用如下所示的TreeNode 替换现有架构会变得更容易:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多