在.NET中纠正“混合”类型的XML序列化和反序列化
我当前的任务涉及编写用于处理HL7 CDA文件的类库。
这些HL7 CDA文件是具有已定义XML模式的XML文件,因此我使用xsd.exe生成用于XML序列化和反序列化的.NET类。
XML Schema包含各种类型,其中包含mixed =“true”属性 ,指定此类型的XML节点可能包含与其他XML节点混合的普通文本。
其中一种类型的XML模式的相关部分如下所示:
生成的此类型代码如下所示:
/// [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(TypeName="StrucDoc.Paragraph", Namespace="urn:hl7-org:v3")] public partial class StrucDocParagraph { private StrucDocCaption captionField; private object[] itemsField; private string[] textField; private string idField; // ...fields for other attributes... /// public StrucDocCaption caption { get { return this.captionField; } set { this.captionField = value; } } /// [System.Xml.Serialization.XmlElementAttribute("br", typeof(StrucDocBr))] [System.Xml.Serialization.XmlElementAttribute("sub", typeof(StrucDocSub))] [System.Xml.Serialization.XmlElementAttribute("sup", typeof(StrucDocSup))] // ...other possible nodes... public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } } /// [System.Xml.Serialization.XmlTextAttribute()] public string[] Text { get { return this.textField; } set { this.textField = value; } } /// [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")] public string ID { get { return this.idField; } set { this.idField = value; } } // ...properties for other attributes... }
如果我反序列化段落节点如下所示的XML元素:
first line
third line
结果是项目和文本数组的读取方式如下:
itemsField = new object[] { new StrucDocBr(), new StrucDocBr(), }; textField = new string[] { "first line", "third line", };
由此无法确定文本和其他节点的确切顺序。
如果我再次序列化 ,结果看起来如下:
first linethird line
默认的序列化程序只是首先序列化项目,然后是文本。
我尝试在StrucDocParagraph类上实现IXmlSerializable
,以便我可以控制内容的反序列化和序列化,但它相当复杂,因为涉及的类很多,我还没有找到解决方案,因为我不知道是否努力得到回报。
是否有某种简单的解决方法来解决这个问题,或者甚至可以通过IXmlSerializable
进行自定义序列化? 或者我应该只使用XmlDocument
或XmlReader
/ XmlWriter
来处理这些文档?
要解决这个问题,我不得不修改生成的类:
- 将
XmlTextAttribute
从Text
属性移动到Items
属性并添加参数Type = typeof(string)
- 删除
Text
属性 - 删除
textField
字段
结果生成的代码(已修改)如下所示:
/// [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(TypeName="StrucDoc.Paragraph", Namespace="urn:hl7-org:v3")] public partial class StrucDocParagraph { private StrucDocCaption captionField; private object[] itemsField; private string idField; // ...fields for other attributes... /// public StrucDocCaption caption { get { return this.captionField; } set { this.captionField = value; } } /// [System.Xml.Serialization.XmlElementAttribute("br", typeof(StrucDocBr))] [System.Xml.Serialization.XmlElementAttribute("sub", typeof(StrucDocSub))] [System.Xml.Serialization.XmlElementAttribute("sup", typeof(StrucDocSup))] // ...other possible nodes... [System.Xml.Serialization.XmlTextAttribute(typeof(string))] public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } } /// [System.Xml.Serialization.XmlAttributeAttribute(DataType="ID")] public string ID { get { return this.idField; } set { this.idField = value; } } // ...properties for other attributes... }
现在,如果我反序列化段落节点如下所示的XML元素:
first line
third line
结果是项目数组的读取方式如下:
itemsField = new object[] { "first line", new StrucDocBr(), new StrucDocBr(), "third line", };
这正是我需要的 ,项目的顺序和内容是正确的 。
如果我再次序列化 ,结果再次正确:
first line
third line
让我指向正确方向的是Guillaume的答案,我也认为必须这样。 然后在MSDN文档中有这个到XmlTextAttribute
:
您可以将XmlTextAttribute应用于返回字符串数组的字段或属性。 您还可以将该属性应用于Object类型的数组,但必须将Type属性设置为string。 在这种情况下,插入到数组中的任何字符串都被序列化为XML文本。
所以序列化和反序列化工作现在正确,但我不知道是否还有其他副作用。 也许不可能再用xsd.exe从这些类生成一个模式,但我还是不需要它。
我有同样的问题,并遇到了改变xsd.exe生成的.cs的解决方案。 虽然它确实有效,但我对改变生成的代码感到不舒服,因为我需要记住在重新生成类的任何时候都要这样做。 它还导致了一些尴尬的代码,必须为mailto元素测试并转换为XmlNode []。
我的解决方案是重新考虑xsd。 我放弃了混合型的使用,基本上定义了我自己的混合型。
我有这个
XML: some text me@email.com some more text
并改为
XML: some text me@email.com some more text
我生成的代码现在给了我一个myText类:
public partial class myText{ private object[] itemsField; /// [System.Xml.Serialization.XmlElementAttribute("mailto", typeof(myTextTextMailto))] [System.Xml.Serialization.XmlElementAttribute("text", typeof(myTextText))] public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } } }
现在,在serilization / deserialisation中保留了元素的顺序,但是我必须针对myTextTextMailto
和myTextText
类型测试/ cast to / program。
我以为我会把它作为替代方法,对我有用。
关于什么
itemsField = new object[] { "first line", new StrucDocBr(), new StrucDocBr(), "third line", };
?