【问题标题】:After importing DataSet from XML, new rows are not nested properly从 XML 导入 DataSet 后,新行未正确嵌套
【发布时间】:2019-09-19 00:10:33
【问题描述】:

我正在使用DataSet.ReadXml() 将 XML 文件导入新的数据集。然后我在 DataSet 中的一个表中添加一个新行,然后我想再次将该 DataSet 导出到 XML。问题是新行没有正确嵌套,只是被附加到 XML 文件的末尾。

这是程序:

    using System;
    using System.Data;
    using System.IO;
    using System.Xml;

    public class Program
    {
        public static void Main()
        {
            string xml = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
    <DATAPACKET Version=""2.0"">
        <METADATA>
            <FIELDS>
                <FIELD attrname=""CompanyID"" fieldtype=""string"" WIDTH=""10""/>
                <FIELD attrname=""Description"" fieldtype=""string"" WIDTH=""40""/>
            </FIELDS>
            <PARAMS/>
        </METADATA>
        <ROWDATA>
            <ROW CompanyID=""CC"" Description=""Contoso""/>
        </ROWDATA>
    </DATAPACKET>
    ";
            XmlReader reader = XmlReader.Create(new StringReader(xml));
            DataSet dataSet = new DataSet();
            dataSet.ReadXml(reader, XmlReadMode.InferTypedSchema);
            var rowTable = dataSet.Tables["ROW"];
            var newRow = rowTable.NewRow();
            newRow["CompanyID"] = "APPL";
            newRow["Description"] = "Apple";
            rowTable.Rows.Add(newRow);
            Console.WriteLine(dataSet.GetXml());
        }
    }

这是输出:

    <DATAPACKET Version="2.0">
      <METADATA>
        <PARAMS />
        <FIELDS>
          <FIELD attrname="CompanyID" fieldtype="string" WIDTH="10" />
          <FIELD attrname="Description" fieldtype="string" WIDTH="40" />
        </FIELDS>
      </METADATA>
      <ROWDATA>
        <ROW CompanyID="CC" Description="Contoso" />
      </ROWDATA>
    </DATAPACKET>
    <ROW CompanyID="APPL" Description="Apple" />

我想要的是将新行与该表中的其他行嵌套,如下所示:

    <DATAPACKET Version="2.0">
      <METADATA>
        <PARAMS />
        <FIELDS>
          <FIELD attrname="CompanyID" fieldtype="string" WIDTH="10" />
          <FIELD attrname="Description" fieldtype="string" WIDTH="40" />
        </FIELDS>
      </METADATA>
      <ROWDATA>
        <ROW CompanyID="CC" Description="Contoso" />
        <ROW CompanyID="APPL" Description="Apple" />
      </ROWDATA>
    </DATAPACKET>

我做错了什么? 如何从 DataSet.GetXml() 中获得格式良好的 XML?

Here is the program running over at dotnetfiddle

【问题讨论】:

  • 进行两处更改 1)From : var newRow = rowTable.NewRow();到: var newRow = rowTable.Rows.Add(); 2)删除:rowTable.Rows.Add(newRow);
  • 我尝试了您建议的编辑,得到了相同的结果。请参阅此处的小提琴以获取输出:dotnetfiddle.net/IjFgtA

标签: c# xml system.data


【解决方案1】:

ReadXml 将您的 xml 分成许多表。 ReadXml 使用以下嵌套标签 1) 数据集名称 2) 数据表名称 3) 行数据:列名是标签,内文是值

请参阅下面的代码,它使用 xml linq 解析 xml:

using System;
using System.Data;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
    class Program
    {
        public static void Main()
        {
            string xml = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
    <DATAPACKET Version=""2.0"">
        <METADATA>
            <FIELDS>
                <FIELD attrname=""CompanyID"" fieldtype=""string"" WIDTH=""10""/>
                <FIELD attrname=""Description"" fieldtype=""string"" WIDTH=""40""/>
            </FIELDS>
            <PARAMS/>
        </METADATA>
        <ROWDATA>
            <ROW CompanyID=""CC"" Description=""Contoso""/>
        </ROWDATA>
    </DATAPACKET>
    ";

            XmlReader reader = XmlReader.Create(new StringReader(xml));
            DataSet dataSet = new DataSet();
            dataSet.ReadXml(reader, XmlReadMode.InferTypedSchema);
            var rowTable = dataSet.Tables["ROW"];
            var newRow = rowTable.NewRow();
            newRow["CompanyID"] = "APPL";
            newRow["Description"] = "Apple";
            rowTable.Rows.Add(newRow);
            Console.WriteLine(dataSet.GetXml());


            XDocument doc = XDocument.Parse(xml);

            DataTable rowTable2 = new DataTable("Table1");
            DataRow newRow2 = null;
            foreach (XElement field in doc.Descendants("FIELD"))
            {
                string t = (string)field.Attribute("fieldtype");
                Type _type = null;
                switch (t)
                {
                    case "string" :
                        _type = typeof(string);
                        break;
                }

                rowTable2.Columns.Add((string)field.Attribute("attrname"), _type);
            }
            foreach (XElement row in doc.Descendants("ROW"))
            {
                newRow = rowTable2.Rows.Add();
                foreach (XAttribute attribute in row.Attributes())
                {
                    newRow[attribute.Name.LocalName] = (string)attribute;
                }
            }
            newRow = rowTable2.Rows.Add();
            newRow["CompanyID"] = "APPL";
            newRow["Description"] = "Apple";
            DataSet ds = new DataSet();
            ds.Tables.Add(rowTable2);
            Console.WriteLine(ds.GetXml());
        }
    }
}

【讨论】:

    【解决方案2】:

    您从哪里获得该 XML?它以DataSet 不支持的方式格式化。嵌套表时,必须定义表之间的父子关系,并且必须将子表的Nested属性设置为true。在您的 XML 中,DataSet 不知道新子行属于哪个父级,因此它将其附加到末尾。

    您可以在 MSDN 中阅读有关 Nesting DataRelations 的信息。

    话虽如此,您的 XML 实际上并没有父表和子表。它有METADATAROWDATA。就像我说的,DataSet 不支持这种格式,您必须将元数据移动到Schema (XSD)。您可以在 MSDN 中阅读有关 Deriving DataSet Relational Structure from XML Schema 的信息。

    以下是您如何使用 XSD 和 XML 表示相关数据的示例:

    using System;
    using System.Data;
    using System.IO;
    using System.Xml;
    
    public class Program
    {
        public static void Main()
        {
            string xml = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
    <MyDataSet>
        <Companies>
            <CompanyID>CC</CompanyID>
            <Description>Contoso</Description>
        </Companies>
    </MyDataSet>
    ";
            string xsd = @"<?xml version=""1.0"" encoding=""utf-8""?>
                <xs:schema id=""SomeID""
                xmlns=""""
                xmlns:xs=""http://www.w3.org/2001/XMLSchema""
                xmlns:msdata=""urn:schemas-microsoft-com:xml-msdata"">
       <xs:element name=""MyDataSet"" msdata:IsDataSet=""true"">
         <xs:complexType>
           <xs:choice minOccurs=""0"" maxOccurs=""unbounded"">
             <xs:element name=""Companies"">
               <xs:complexType >
                 <xs:sequence>
                   <xs:element name=""CompanyID"" type=""xs:string"" minOccurs=""0"" />
                   <xs:element name=""Description"" type=""xs:string"" minOccurs=""0"" />  
                 </xs:sequence>
               </xs:complexType>
              </xs:element>
           </xs:choice>
         </xs:complexType>
       </xs:element>
     </xs:schema>
    ";
            DataSet dataSet = new DataSet();
            StringReader sr = new StringReader(xsd);
            dataSet.ReadXmlSchema(sr);
            sr = new StringReader(xml);
            dataSet.ReadXml(sr, XmlReadMode.InferTypedSchema);
            var rowTable = dataSet.Tables["Companies"];
            var newRow = rowTable.NewRow();
            newRow["CompanyID"] = "APPL";
            newRow["Description"] = "Apple";
            rowTable.Rows.Add(newRow);
            Console.WriteLine(dataSet.GetXml());
        }
    }
    

    在这种情况下,您实际上并不需要 Schema,因为您只有一个表,其中所有列都为 string。因此,如果您从上面的代码中删除 Schema 并再次运行它,您将获得完全相同的结果。但是,这让您了解如何使用 Schema 定义 DataSet 结构,因此您可以添加更复杂的表以及它们之间的关系。对于没有关系的简单表,您不需要 Schema。

    【讨论】:

    • 不幸的是,我无法控制传入的 XML 文件的格式。因此,尝试修改架构无济于事。当将DataSet.ReadXml()XmlReadMode.InferTypedSchema 一起使用时,我得到的架构是DataSet.GetXmlSchema() 的结果,这意味着它正在使用正确的父子关系构造DataSet。查看输出的小提琴:dotnetfiddle.net/Widget/2az4kc
    • 不,你没有得到一个“有意义的模式”。您没有正确理解架构。这就是为什么你会惊讶于最后添加新记录的原因。你得到的是 6 个毫无意义的表格。将最后一行更改为 Console.WriteLine(dataSet.Tables.Count); 并亲自查看。现在这对你有意义吗?这 6 个表相互嵌套,没有定义的关系。此 XML 对 DataSet 无效,因此如果您无法控制它,则必须使用 XDocument 并逐行手动解析。
    【解决方案3】:

    我解决了我自己的问题。问题是我不知道表之间自动生成的关系创建了需要填充的外键列。 对于ROW 表,自动生成的外键是ROWDATA_Id

    这是按预期工作的更新代码:

    using System;
    using System.Data;
    using System.IO;
    using System.Xml;
    
    public class Program
    {
        public static void Main()
        {
            string xml = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
    <DATAPACKET Version=""2.0"">
        <METADATA>
            <FIELDS>
                <FIELD attrname=""CompanyID"" fieldtype=""string"" WIDTH=""10""/>
                <FIELD attrname=""Description"" fieldtype=""string"" WIDTH=""40""/>
            </FIELDS>
            <PARAMS/>
        </METADATA>
        <ROWDATA>
            <ROW CompanyID=""CC"" Description=""Contoso""/>
        </ROWDATA>
    </DATAPACKET>
    ";
            XmlReader reader = XmlReader.Create(new StringReader(xml));
            DataSet dataSet = new DataSet();
            dataSet.ReadXml(reader, XmlReadMode.InferTypedSchema);
            var rowTable = dataSet.Tables["ROW"];
            var newRow = rowTable.NewRow();
            newRow["CompanyID"] = "APPL";
            newRow["Description"] = "Apple";
            newRow["ROWDATA_Id"] = 0; //This is what I was missing. This nests the row properly
            rowTable.Rows.Add(newRow);
            Console.WriteLine(dataSet.GetXml());
        }
    }
    

    另一种解决方案是将外键列ROWDATA_IdDataColumn.DefaultValue 设置为0

    var rowTable = dataSet.Tables["ROW"];
    rowTable.Columns["ROWDATA_Id"].DefaultValue = 0;
    

    这是两种解决方案的输出:

    <DATAPACKET Version="2.0">
      <METADATA>
        <PARAMS />
        <FIELDS>
          <FIELD attrname="CompanyID" fieldtype="string" WIDTH="10" />
          <FIELD attrname="Description" fieldtype="string" WIDTH="40" />
        </FIELDS>
      </METADATA>
      <ROWDATA>
        <ROW CompanyID="CC" Description="Contoso" />
        <ROW CompanyID="APPL" Description="Apple" />
      </ROWDATA>
    </DATAPACKET>
    

    Here is the first solution running on dotnetfiddle

    Here is the alternate solution running on dotnetfiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-19
      相关资源
      最近更新 更多