在从数据创建的XML结构中面临问题

本文关键字:问题 结构 XML 数据 创建 | 更新日期: 2023-09-27 17:59:14

我想通过数据创建XML,并且我有从数据库接收到DataSetIList<Booking>的数据。现在我正在使用DataSet通过此代码创建XML。

string result = String.Empty;
using (StringWriter sw = new StringWriter())
 {
   ds.WriteXml(sw);
   result = sw.ToString();
 }

我的XML是这种形式。

<Booking> 
    <ID>32</ID> 
    <BookingNumber>12120001</BLNumber> 
    <ReferenceNo>ABCED11212280007</ReferenceNo> 
    <Name>Customer Name1</Name> 
    <Address>Customer Address</Address>
</Booking>
<Booking> 
    <ID>33</ID> 
    <BookingNumber>12120002</BLNumber> 
    <ReferenceNo>ABCED11212280008</ReferenceNo> 
    <Name>Customer Name2</Name> 
    <Address>Customer Address2</Address>
</Booking>

<BookingDetail> 
   <ID>206</ID> 
   <BookingID>32</BookingID> 
   <OrderItem>Item1</OrderItem> 
</BookingDetail>
<BookingDetail> 
   <ID>207</ID> 
   <BookingID>32</BookingID> 
   <OrderItem>Item2</OrderItem> 
</BookingDetail>
<BookingDetail> 
   <ID>208</ID> 
   <BookingID>33</BookingID> 
   <OrderItem>Item1</OrderItem> 
</BookingDetail>
<BookingDetail> 
   <ID>209</ID> 
   <BookingID>33</BookingID> 
   <OrderItem>Item2</OrderItem> 
</BookingDetail>
<BookingDetail> 
   <ID>210</ID> 
   <BookingID>33</BookingID> 
   <OrderItem>Item3</OrderItem> 
</BookingDetail>

但是我想要这种形式的XML。

<CompleteBooking>
 <Booking> 
    <ID>32</ID> 
    <BookingNumber>12120001</BLNumber> 
    <ReferenceNo>ABCED11212280007</ReferenceNo> 
    <Name>Customer Name1</Name> 
    <Address>Customer Address</Address>
 </Booking>
 <BookingDetail> 
   <ID>206</ID> 
   <BookingID>32</BookingID> 
   <OrderItem>Item1</OrderItem> 
 </BookingDetail>
 <BookingDetail> 
   <ID>207</ID> 
   <BookingID>32</BookingID> 
   <OrderItem>Item2</OrderItem> 
 </BookingDetail>
</CompleteBooking>
<CompleteBooking>
 <Booking> 
    <ID>33</ID> 
    <BookingNumber>12120002</BLNumber> 
    <ReferenceNo>ABCED11212280008</ReferenceNo> 
    <Name>Customer Name2</Name> 
    <Address>Customer Address2</Address>
 </Booking>
 <BookingDetail> 
   <ID>208</ID> 
   <BookingID>33</BookingID> 
   <OrderItem>Item1</OrderItem> 
 </BookingDetail>
 <BookingDetail> 
   <ID>209</ID> 
   <BookingID>33</BookingID> 
   <OrderItem>Item2</OrderItem> 
 </BookingDetail>
 <BookingDetail> 
   <ID>210</ID> 
   <BookingID>33</BookingID> 
   <OrderItem>Item3</OrderItem> 
 </BookingDetail>
</CompleteBooking>

任何人都可以帮助我创建这种类型的XML吗?

在从数据创建的XML结构中面临问题

使用 System.Xml.Serialization,创建预订的对象:

public class XMLEntities
{   
  [XmlRoot(ElementName = "CompleteBooking")]
  public class CompleteBooking
  {
    [XmlElement(ElementName = "Booking")]
    public Booking Bookings { get; set; }
    [XmlElement(ElementName = "BookingDetail")]
    public List<BookingDetail> BookingDetail { get; set; }
  }
  public class Booking
  {
    [XmlElement("ID")]
    public int ID { get; set; }
    [XmlElement("BookingNumber")]
    public int BookingNumber { get; set; }
    [XmlElement("ReferenceNumber")]
    public string ReferenceNumber { get; set; }
    [XmlElement("Name")]
    public string Name { get; set; }
    [XmlElement("Address")]
    public string Address { get; set; }
  }
  public class BookingDetail
  {
    [XmlElement("ID")]
    public int ID { get; set; }
    [XmlElement("BookingID")]
    public int BookingID { get; set; }
    [XmlElement("OrderItem")]
    public string OrderItem { get; set; }
  }
}

现在,对于序列化程序对象(用于将对象实际序列化为字符串(:

 public class XMLEntitiesSerializer
 {
   public string Serialize(XMLEntities.CompleteBooking completeBooking)
   {
     var serializedXml = string.Empty;
     var serializer = new XmlSerializer(typeof (XMLEntities.CompleteBooking));
     var stringWriter = new System.IO.StringWriter();
     try
     {
       serializer.Serialize(stringWriter, completeBooking);
       serializedXml = stringWriter.ToString();
     }
     catch(Exception ex)
     {
       //Log the stuff
     }
     finally
     {
       stringWriter.Close();
     }
     return serializedXml;
   }
 }

现在,您只需创建正确定义的对象并在某种函数中进行序列化。 例如,在控制台应用程序的主要方法中:

public static void Main(string[] args)
{
  //Create new booking objects
  var booking1 = new XMLEntities.Booking()
                  {
                    ID = 32,
                    BookingNumber = 1212001,
                    ReferenceNumber = "ABCED11212280007",
                    Name = "Customer Name1",
                    Address = "Customer Address"
                  };
  var booking2 = new XMLEntities.Booking()
                   {
                     ID = 33,
                     BookingNumber = 12120002,
                     ReferenceNumber = "ABCED11212280008",
                     Name = "Customer Name2",
                     Address = "Customer Address2"
                   };
  //Create the booking details objects
  var booking1Detail1 = new XMLEntities.BookingDetail()
                         {
                           ID = 206,
                           BookingID = 32,
                           OrderItem = "Item1"
                         };
  var booking1Detail2 = new XMLEntities.BookingDetail()
                          {
                            ID = 207,
                            BookingID = 32,
                            OrderItem = "Item2"
                          };
  var booking2Detail1 = new XMLEntities.BookingDetail()
                          {
                            ID = 208,
                            BookingID = 33,
                            OrderItem = "Item1"
                          };
  var booking2Detail2 = new XMLEntities.BookingDetail()
                          {
                            ID = 209,
                            BookingID = 32,
                            OrderItem = "Item2"
                          };
  var booking2Detail3 = new XMLEntities.BookingDetail()
                          {
                            ID = 210,
                            BookingID = 32,
                            OrderItem = "Item3"
                          };
  //Smash them together so we can serialize as one
  var completeBooking1 = new XMLEntities.CompleteBooking()
                          {
                            Bookings = booking1,
                            BookingDetail = new List<XMLEntities.BookingDetail>()
                                              {
                                                booking1Detail1,
                                                booking1Detail2
                                              }
                          };
  var completeBooking2 = new XMLEntities.CompleteBooking()
                           {
                             Bookings = booking2,
                             BookingDetail = new List<XMLEntities.BookingDetail>()
                                               {
                                                 booking2Detail1,
                                                 booking2Detail2,
                                                 booking2Detail3
                                               }
                           };
  //Serialize the data for each of the booking objects
  var serializedXML = new XMLEntitiesSerializer();
  var xml = string.Empty;
  var booking1XmlString = serializedXML.Serialize(completeBooking1);
  var booking2XmlString = serializedXML.Serialize(completeBooking2);
  Console.ReadLine();
}

显然,您可以在重构函数中使用它(这将使生活更轻松(,但这为您提供了您正在寻找的一般输出。

这是一种可能的方法:

        DataTable Booking = new DataTable();
        Booking.Columns.AddRange(new DataColumn[]{ new DataColumn("ID"), new DataColumn("BookingNumber"), new DataColumn("ReferenceNo"), new DataColumn("Name"), new DataColumn("Address") });
        DataTable BookingDetail = new DataTable();
        BookingDetail.Columns.AddRange(new DataColumn[] { new DataColumn("ID"), new DataColumn("BookingID"), new DataColumn("OrderItem") });
        Booking.Rows.Add(32, 12120001, "ABCED11212280007", "Customer Name1", "Customer Address");
        BookingDetail.Rows.Add(206, 32, "Item1");
        BookingDetail.Rows.Add(207, 32, "Item2");
        Booking.Rows.Add(33, 12120002, "ABCED11212280008", "Customer Name2", "Customer Address2");
        BookingDetail.Rows.Add(208, 33, "Item1");
        BookingDetail.Rows.Add(209, 33, "Item2");
        BookingDetail.Rows.Add(210, 33, "Item3");
        XElement root = new XElement("Root");
        // For each row from Booking add one CompleteBooking element
        foreach(DataRow BookingRow in Booking.Rows.Cast<DataRow>())
        {
            XElement xeCompleteBooking = new XElement("CompleteBooking");
            XElement xeBooking = new XElement("Booking");
            int BookingID = Convert.ToInt32(BookingRow["ID"]);
            IEnumerable<string> columnNames_Booking = Booking.Columns.Cast<DataColumn>().Select(col => col.ColumnName);
            // Add element under Booking element for every column of table
            foreach (string colName in columnNames_Booking)
                xeBooking.Add(new XElement(colName, BookingRow[colName]));
            xeCompleteBooking.Add(xeBooking);
            IEnumerable<string> columnNames_BookingDetail = BookingDetail.Columns.Cast<DataColumn>().Select(col => col.ColumnName);
            // For Booking.ID find all BookingDetail rows according to BookingDetail.BookingID
            IEnumerable<DataRow> details = BookingDetail.Rows.Cast<DataRow>().Where(BookingDetailRow => Convert.ToInt32(BookingDetailRow["BookingID"]) == BookingID);
            foreach (DataRow BookingDetailRow in details)
            {
                XElement xeBookingDetail = new XElement("BookingDetail");
                // Add element under BookingDetail element for every column of table
                foreach (string colName in columnNames_BookingDetail)
                    xeBookingDetail.Add(new XElement(colName, BookingDetailRow[colName]));
                xeCompleteBooking.Add(xeBookingDetail);
            }
            root.Add(xeCompleteBooking);
        }
        string xml = root.ToString();

它使用 LINQ to XML。它读取列名以创建适当命名的XML元素,因此,如果您在表中添加或删除某些列,这不应该分解,唯一应该具有固定列名的列是ID(Booking(和BookingID(BookingDetail(,因为它们用于链接两个表。

不能对

数据对象使用 XmlSerializer 类?还是我误解了这个问题?

在没有看到一些对象结构的情况下,除了提供一些"如果我在你的鞋子里......"之外,我几乎无能为力。

你想要的是"非标准"XML,因为当一个是列表(BookingDetail(时,在同一标签下有两种类型的对象"Booking"和"BookingDetail"。

如果必须采用这种形式,我的做法是手动序列化:

public String Serialize(CompleteBooking [] cbs) {
    String FinalXML = "<CompleteBookings>";
    foreach(CompleteBooking cb in cbs) {
        FinalXML += cb.ToXML();
    }
    FinalXML += "</CompleteBookings>";
}

数据对象:

public class CompleteBooking {
    public Booking Booking;
    public BookingDetail [] BookingDetails
    public String ToXML() {
        String RVal = "<CompleteBooking>" + this.Booking.ToXML();
        foreach(BookingDetail bd in BookingDetails) {
            RVal += bd.ToXML();
        }
        RVal += "</CompleteBooking>"

    }
}
public class Booking {
    // Fields Here
    public String ToXML() {
        return "<Booking>" + [Fields] + "</Booking>";
    }
}
public class BookingDetail {
    // Fields Here
    public String ToXML() {
        return "<BookingDetail>" + [Fields] + "</BookingDetail>";
    }
}

假设我们有以下数据类:

class Booking
{
    public int ID { get; set;}
    public int BookingNumber { get; set;}
    public string ReferenceNo { get; set;}
    public string Name { get; set;}
    public string Address { get; set;}
}
class BookingDetails
{
    public int ID { get; set;}
    public int BookingId { get; set;}
    public string OrderItem { get; set;}
}

以及以下测试数据:

    static private IList<Booking> _bookings = new List<Booking>() {
        new Booking() { ID = 32, BookingNumber = 12120001, ReferenceNo = "ABCED11212280007", Name = "Customer Name1", Address = "Customer Address" },
        new Booking() { ID = 33, BookingNumber = 12120002, ReferenceNo = "ABCED11212280008", Name = "Customer Name2", Address = "Customer Address2" }
    };
    static private IList<BookingDetails> _details = new List<BookingDetails>() {
        new BookingDetails() { ID = 206, BookingId = 32, OrderItem = "Item1" },
        new BookingDetails() { ID = 207, BookingId = 32, OrderItem = "Item2" },
        new BookingDetails() { ID = 208, BookingId = 33, OrderItem = "Item1" },
        new BookingDetails() { ID = 209, BookingId = 33, OrderItem = "Item2" },
        new BookingDetails() { ID = 210, BookingId = 33, OrderItem = "Item3" }
    };

我们可以通过以下 Linq to XML 查询轻松获取给定格式的输出 XML:

var bookings = _bookings.Join(_details, b => b.ID, d => d.BookingId, (b, d) => new { b, d })
                        .GroupBy(g => g.b, g => g.d)
                        .Select(g => new XElement("CompleteBooking",
                                        new XElement("Booking", 
                                            new XElement("ID", g.Key.ID),
                                            new XElement("BookingNumber", g.Key.BookingNumber),
                                            new XElement("ReferenceNo", g.Key.ReferenceNo),
                                            new XElement("Name", g.Key.Name),
                                            new XElement("Address", g.Key.Address)),
                                        g.Select(d => new XElement("BookingDetail",
                                                            new XElement("ID", d.ID),
                                                            new XElement("BookingID", d.BookingId),
                                                            new XElement("OrderItem", d.OrderItem))).ToArray())).ToArray();

它会给我们一个XElement对象的数组。要获取 xml 字符串,请使用String.Join<XElement>方法:

var xmlString = String.Join<XElement>(Environment.NewLine, bookings);

但是,我建议使用稍微不同的XML模式:

<Bookings>
  <Booking>
    <ID>32</ID>
    <BookingNumber>12120001</BookingNumber>
    <ReferenceNo>ABCED11212280007</ReferenceNo>
    <Name>Customer Name1</Name>
    <Address>Customer Address</Address>
    <Details>
      <Detail>
        <ID>206</ID>
        <OrderItem>Item1</OrderItem>
      </Detail>
      <Detail>
        <ID>207</ID>
        <OrderItem>Item2</OrderItem>
      </Detail>
    </Details>
  </Booking>
  <Booking>
    <ID>33</ID>
    <BookingNumber>12120002</BookingNumber>
    <ReferenceNo>ABCED11212280008</ReferenceNo>
    <Name>Customer Name2</Name>
    <Address>Customer Address2</Address>
    <Details>
      <Detail>
        <ID>208</ID>
        <OrderItem>Item1</OrderItem>
      </Detail>
      <Detail>
        <ID>209</ID>
        <OrderItem>Item2</OrderItem>
      </Detail>
      <Detail>
        <ID>210</ID>
        <OrderItem>Item3</OrderItem>
      </Detail>
    </Details>
  </Booking>
</Bookings>

这种格式的数据中没有冗余。要获取它,请使用以下查询:

var bookings = _bookings.Join(_details, b => b.ID, d => d.BookingId, (b, d) => new { b, d })
                        .GroupBy(g => g.b, g => g.d)
                        .Select(g => new XElement("Booking", 
                                        new XElement("ID", g.Key.ID),
                                        new XElement("BookingNumber", g.Key.BookingNumber),
                                        new XElement("ReferenceNo", g.Key.ReferenceNo),
                                        new XElement("Name", g.Key.Name),
                                        new XElement("Address", g.Key.Address),
                                        new XElement("Details",
                                            g.Select(d => new XElement("Detail",
                                                              new XElement("ID", d.ID),
                                                              new XElement("OrderItem", d.OrderItem))).ToArray()))).ToArray();
var data = new XDocument(new XElement("Bookings", bookings));

您可以使用 LINQ to XML 创建自定义结构。使用嵌套的 XElement 构造函数。例如,请参阅如何创建此结构的 XML 和使用 c# 动态生成 XML

很抱歉,在我对你的问题的评论之后有一个迟来的答案,@Umair Noor。

由于您正在使用 .NET DataSet,以下是我知道从中获取所需 XML 的最简单方法 - 以及相关的在线参考:

  1. 创建一个表示要查看的 XML 输出的 XML 文档。 您已经在问题中完成此操作您需要 1( 将<'BLNumber>结束标记替换为 <'BookingNumber> 标记,以及 2( 解决@ByteBlast关于示例中多个根元素的观点。

    以下是我通过最低限度地调整您想要的 XML 示例而快速得出的结果:

    <Bookings> <!-- fix for multiple root elements -->
      <CompleteBooking>
        <Booking>
          <ID>32</ID>
          <BookingNumber>12120001</BookingNumber> <!-- fixed BLNumber closing tag -->
          <ReferenceNo>ABCED11212280007</ReferenceNo>
          <Name>Customer Name1</Name>
          <Address>Customer Address</Address>
        </Booking>
        <BookingDetail>
          <ID>206</ID>
          <BookingID>32</BookingID>
          <OrderItem>Item1</OrderItem>
        </BookingDetail>
        <BookingDetail>
          <ID>207</ID>
          <BookingID>32</BookingID>
          <OrderItem>Item2</OrderItem>
        </BookingDetail>
      </CompleteBooking>
      <CompleteBooking>
        <Booking>
          <ID>33</ID>
          <BookingNumber>12120002</BookingNumber> <!-- fixed BLNumber closing tag -->
          <ReferenceNo>ABCED11212280008</ReferenceNo>
          <Name>Customer Name2</Name>
          <Address>Customer Address2</Address>
        </Booking>
        <BookingDetail>
          <ID>208</ID>
          <BookingID>33</BookingID>
          <OrderItem>Item1</OrderItem>
        </BookingDetail>
        <BookingDetail>
          <ID>209</ID>
          <BookingID>33</BookingID>
          <OrderItem>Item2</OrderItem>
        </BookingDetail>
        <BookingDetail>
          <ID>210</ID>
          <BookingID>33</BookingID>
          <OrderItem>Item3</OrderItem>
        </BookingDetail>
      </CompleteBooking>
    </Bookings> <!-- fix for multiple root elements -->
    
  2. 使用xsd.exe命令行工具从代表性 XML 文件生成 XML 架构定义 (XSD( 文件。

    或者,编写几行代码来读取 XML 文件,使用 XmlReadMode.InferSchema 为其生成架构;并将生成的架构写入 XSD 文件。 例如,下面是我快速放入 WPF 临时应用中的按钮事件处理程序的内容:

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        // BEGIN: the bottom-line logic
        var ds = new DataSet();
        var sr = new StreamReader("Bookings.xml"); // file into which I put your sample, desired XML
        ds.ReadXml(sr, XmlReadMode.InferSchema); // XmlReadMode.InferSchema - key
        sr.Close();
        ds.WriteXmlSchema("Bookings.xsd"); // file into which I got the resulting schema
        // END: the bottom-line logic
    }
    

    这两种方法都足够快。

  3. 使用 XSD 文件...

    <?xml version="1.0" standalone="yes"?>
    <xs:schema id="Bookings" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
      <xs:element name="Bookings" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
        <xs:complexType>
          <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="CompleteBooking">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="Booking" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType>
                      <xs:sequence>
                        <xs:element name="ID" type="xs:string" minOccurs="0" />
                        <xs:element name="BookingNumber" type="xs:string" minOccurs="0" />
                        <xs:element name="ReferenceNo" type="xs:string" minOccurs="0" />
                        <xs:element name="Name" type="xs:string" minOccurs="0" />
                        <xs:element name="Address" type="xs:string" minOccurs="0" />
                      </xs:sequence>
                    </xs:complexType>
                  </xs:element>
                  <xs:element name="BookingDetail" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType>
                      <xs:sequence>
                        <xs:element name="ID" type="xs:string" minOccurs="0" />
                        <xs:element name="BookingID" type="xs:string" minOccurs="0" />
                        <xs:element name="OrderItem" type="xs:string" minOccurs="0" />
                      </xs:sequence>
                    </xs:complexType>
                  </xs:element>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:choice>
        </xs:complexType>
      </xs:element>
    </xs:schema>
    

    。定义类型化DataSet(例如 BookingsBookingsDataSet ( - 而不是您可能使用的通用DataSet

    可以将xsd.exe/d[ataset]选项与步骤 3 中的 XSD 文件一起使用,也可以将新的DataSet项添加到 Visual Studio 中的项目中,然后将步骤 3 派生的架构粘贴到其.xsd文件中。

    相同 - 无论哪种方式都足够快。

  4. 根据需要重复,直到获得所需的确切结果。

为了获得权威参考,MSDN 在 ADO.NET 中对 XML 有一个很好的概述,它解释了我所描述的大部分内容。