序列化/反序列化 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()
中,您将 X
和 Y
的值写为嵌套的 XML 元素 。这就解释了NullReferenceException
。您需要修复您的读取和写入方法以保持一致。
但是,我建议采用替代实施 IXmlSerializable
的方法,即为您的 Company
引入 代理类型 。首先,extract将Company
的所有非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; }
}
我有两个 类、"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()
中,您将 X
和 Y
的值写为嵌套的 XML 元素 。这就解释了NullReferenceException
。您需要修复您的读取和写入方法以保持一致。
但是,我建议采用替代实施 IXmlSerializable
的方法,即为您的 Company
引入 代理类型 。首先,extract将Company
的所有非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
:
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; }
}