XSD转DataSet再生成XML时如何保留元素顺序通过XSD校验?
解决方案
DataSet的GetXml()方法本身不会保留XSD定义的元素顺序,因为它基于数据表结构输出:先输出所有列(简单类型),再输出子表对应的复杂类型元素。以下是两种可行的解决方法:
方法1:使用XmlWriter自定义序列化逻辑
直接基于DataSet的数据,按照XSD规定的顺序手动写入XML元素,严格控制输出顺序,这是最直接可靠的方式。
修正后的代码
var schemaString = @"<xs:schema xmlns:xs=""http://www.w3.org/2001/XMLSchema"" > <xs:element name=""Person""> <xs:complexType> <xs:sequence> <xs:element name=""Ttl"" minOccurs=""0""> <xs:simpleType> <xs:restriction base=""xs:string""> <xs:maxLength value=""4"" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element minOccurs=""0"" maxOccurs=""1"" name=""Address""> <xs:complexType> <xs:sequence> <xs:element minOccurs=""1"" maxOccurs=""4"" name=""Line_1""> <xs:simpleType> <xs:restriction base=""xs:string""> <xs:maxLength value=""35"" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element minOccurs=""1"" maxOccurs=""4"" name=""Line_2""> <xs:simpleType> <xs:restriction base=""xs:string""> <xs:maxLength value=""35"" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element minOccurs=""0"" maxOccurs=""1"" name=""PostCode""> <xs:simpleType> <xs:restriction base=""xs:string""> <xs:maxLength value=""35"" /> </xs:restriction> </xs:simpleType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <xs:element name=""Fore"" maxOccurs=""2""> <xs:simpleType> <xs:restriction base=""xs:string""> <xs:maxLength value=""35"" /> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name=""Sur""> <xs:simpleType> <xs:restriction base=""xs:string""> <xs:maxLength value=""35"" /> </xs:restriction> </xs:simpleType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>"; var schemaReader = XmlReader.Create(new StringReader(schemaString)); var dataSet = new DataSet(); dataSet.ReadXmlSchema(schemaReader); dataSet.Tables["Person"].Rows.Add("Mr", "John", "Smith"); dataSet.Tables["Address"].Rows.Add("Street", "Suburb", "1234"); dataSet.Tables["Address"].Rows[0].SetParentRow(dataSet.Tables["Person"].Rows[0]); // 手动生成符合顺序的XML var sb = new StringBuilder(); using var writer = XmlWriter.Create(sb, new XmlWriterSettings { Indent = true }); writer.WriteStartElement("NewDataSet"); foreach (DataRow personRow in dataSet.Tables["Person"].Rows) { writer.WriteStartElement("Person"); // 严格按照XSD sequence的顺序输出元素 writer.WriteElementString("Ttl", personRow["Ttl"]?.ToString()); // 输出Address子元素 var addressRows = personRow.GetChildRows(dataSet.Relations[0]); if (addressRows.Length > 0) { var addressRow = addressRows[0]; writer.WriteStartElement("Address"); writer.WriteElementString("Line_1", addressRow["Line_1"]?.ToString()); writer.WriteElementString("Line_2", addressRow["Line_2"]?.ToString()); writer.WriteElementString("PostCode", addressRow["PostCode"]?.ToString()); writer.WriteEndElement(); } writer.WriteElementString("Fore", personRow["Fore"]?.ToString()); writer.WriteElementString("Sur", personRow["Sur"]?.ToString()); writer.WriteEndElement(); } writer.WriteEndElement(); var validXml = sb.ToString(); Console.WriteLine(validXml);
生成的符合校验要求的XML
<NewDataSet> <Person> <Ttl>Mr</Ttl> <Address> <Line_1>Street</Line_1> <Line_2>Suburb</Line_2> <PostCode>1234</PostCode> </Address> <Fore>John</Fore> <Sur>Smith</Sur> </Person> </NewDataSet>
方法2:XSLT转换(适合复杂场景)
如果需要处理大量复杂结构,可以先让DataSet输出包含Schema的XML,再编写XSLT样式表将元素重新排序为XSD要求的顺序。这种方式步骤较多,但适合批量处理场景。
内容的提问来源于stack exchange,提问作者Murray
相关产品推荐
相关产品推荐

