[ACCEPTED]-Best .net Method to create an XML Doc-xml
Josh's answer shows how easy it is to create 11 a single element in LINQ to XML... it doesn't 10 show how it's also hugely easy to create 9 multiple elements. Suppose you have a List<Order>
called 8 orders
... you can create the whole document like 7 this:
var xml = new XElement("Orders",
orders.Select(order =>
new XElement("Order",
new XAttribute("OrderNumber", order.OrderNumber),
new XElement("ItemNumber", order.ItemNumber),
new XElement("QTY", order.Quantity),
new XElement("Warehouse", order.Warehouse)
)
)
);
LINQ to XML makes constructing XML 6 incredibly easy. It also has support for 5 XML namespaces which is pretty easy too. For 4 instance, if you wanted your elements to 3 be in a particular namespace, you'd just 2 need:
XNamespace ns = "http://your/namespace/here";
var xml = new XElement(ns + "Orders",
orders.Select(order =>
new XElement(ns + "Order",
... (rest of code as before)
LINQ to XML is the best XML API I've 1 worked with... it's great for querying too.
I would suggest using the classes in System.Xml.Linq.dll which 16 contain an XML DOM API that allows for easy 15 build-up of XML structures due to the way 14 the contructors are designed. Trying to 13 create an XML structure using the System.Xml 12 classes is very painful because you have 11 to create them detached then separately 10 add them into the document.
Here's an example of XLinq vs. System.Xml 9 to create a DOM from scratch. Your eyes 8 will bleed when you see the System.Xml example.
Here's 7 a quick example of how you would use XLinq 6 to build up part of your doc.
var xml = new XElement("Orders",
new XElement("Order",
new XAttribute("OrderNumber", 12345),
new XElement("ItemNumber", "01234567"),
new XElement("QTY", 10),
new XElement("Warehouse", "PA019")
)
);
TIP Although it's 5 a little unorthodox (though no worse than 4 some of the language butchering that has 3 become popular lately), I have on occasion 2 used C#'s type aliasing feature to minimize 1 the code even further:
using XE = System.Xml.Linq.XElement;
using XA = System.Xml.Linq.XAttribute;
...
var xml = new XE("Orders",
new XE("Order",
new XA("OrderNumber", 12345),
new XA("ItemNumber", "01234567"),
new XA("QTY", 10),
new XA("Warehouse", "PA019")
)
);
What about this : create a class "Order" and 12 one "Orders", and then serialize those out 11 to XML - seems a lot easier to me than creating 10 the XML bit by bit from hand....
Since you 9 say you're pulling the data off your ERP, you 8 probably already have objects and classes 7 for "Order" and so on - maybe it's sufficient 6 to put a few [XmlElement] attributes on 5 your classes and you're good to go!
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace XmlLinqTest
{
[Serializable]
[XmlRoot(Namespace = "")]
public class Orders
{
private List<Order> _orders = new List<Order>();
/// <remarks/>
[XmlElement("Order")]
public List<Order> OrderList
{
get { return _orders; }
}
}
/// <remarks/>
[Serializable]
public class Order
{
/// <remarks/>
[XmlElement]
public string ItemNumber { get; set; }
[XmlElement]
public int QTY { get; set; }
/// <remarks/>
[XmlElement]
public string WareHouse { get; set; }
/// <remarks/>
[XmlAttribute]
public string OrderNumber { get; set; }
}
}
and 4 in your main app something like this:
Orders orders = new Orders();
Order work = new Order() { ItemNumber = "0123993587", OrderNumber = "12345", QTY = 10, WareHouse = "PA019" };
orders.OrderList.Add(work);
work = new Order() { ItemNumber = "0123993587", OrderNumber = "12346", QTY = 9, WareHouse = "PA019" };
orders.OrderList.Add(work);
work = new Order() { ItemNumber = "0123993587", OrderNumber = "12347", QTY = 8, WareHouse = "PA019" };
orders.OrderList.Add(work);
XmlSerializer ser = new XmlSerializer(typeof(Orders));
using(StreamWriter wr = new StreamWriter(@"D:\testoutput.xml", false, Encoding.UTF8))
{
ser.Serialize(wr, orders);
}
Working 3 with objects and then serializing them out 2 to disk seems a lot easier to me than fiddling 1 around with XDocument and other APIs.
If your use case is simple, nothing could 9 possibly simpler and easier to use than 8 XmlTextWriter. That said, one alternative 7 would be to use an XmlDocument object to 6 create and append all of your nodes. But 5 I think it's easier to write and maintain 4 code that uses XmlTextWriter if you're creating 3 a document from scratch rather than manipulating 2 one. Using XmlTextWriter should be very 1 straightforward:
StringBuilder output = new StringBuilder();
XmlWriter writer = XmlWriter.Create(output);
writer.WriteProcessingInstruction("xml", "version=\"1.0\"");
writer.WriteStartElement("Orders");
//...start loop...
writer.WriteStartElement("Order");
writer.WriteAttributeString("OrderNumber", "12345");
writer.WriteElementString("ItemNumber", "0123993587");
writer.WriteElementString("QTY", "10");
writer.WriteElementString("WareHouse", "PA019");
writer.WriteEndElement();
//...loop...
writer.WriteEndElement();
writer.Close();
There's a new language called XCST that compiles 1 to C#.
<c:template name='c:initial-template' expand-text='yes'>
<c:param name='orders' as='IEnumerable<Order>'/>
<Orders>
<c:for-each name='order' in='orders'>
<Order OrderNumber='{order.Number}'>
<ItemNumber>{order.ItemNumber}</ItemNumber>
<QTY>{order.Quantity}</QTY>
<WareHouse>{order.WareHouse}</WareHouse>
</Order>
</c:for-each>
</Orders>
</c:template>
I had to create following XML document having 3 some part of it parametrized.
<?xml version="1.0" encoding="utf-8"?>
<wap-provisioningdoc>
<characteristic type="BOOTSTRAP">
<parm name="NAME" value="SYNCSETTINGS" />
</characteristic>
<characteristic type="APPLICATION">
<parm name="APPID" value="w5" />
<parm name="TO-NAPID" value="INTERNET" />
<parm name="NAME" value="SYNCSETTINGS" />
<parm name="ADDR" value="http://syncserver/sync" />
<characteristic type="RESOURCE">
<parm name="URI" value="pb" />
<parm name="NAME" value="Contacts DB" />
<parm name="AACCEPT" value="text/x-vcard" />
</characteristic>
<characteristic type="RESOURCE">
<parm name="URI" value="cal" />
<parm name="NAME" value="Calendar DB" />
<parm name="AACCEPT" value="text/x-vcalendar" />
</characteristic>
<characteristic type="RESOURCE">
<parm name="URI" value="notes" />
<parm name="NAME" value="Notes DB" />
<parm name="AACCEPT" value="text/plain" />
</characteristic>
<characteristic type="APPAUTH">
<parm name="AAUTHNAME" value="username" />
<parm name="AAUTHSECRET" value="password" />
</characteristic>
</characteristic>
</wap-provisioningdoc>
And here's 2 my code that does this with single LINQ 1 statement.
Make a note that creating XML document above using LINQ and saving it to XML file preserves the XML document formatting and keeps line-breaks and carriage-returns that makes the document properly tabified.
public string CreateOTAXmlFile(string Username, string Password)
{
var ota = new XDocument(
new XElement("wap-provisioningdoc",
new XElement("characteristic", new XAttribute("type", "BOOTSTRAP"),
new XElement("parm", new XAttribute("name", "NAME"), new XAttribute("value", "SYNCSETTINGS"))
),
new XElement("characteristic", new XAttribute("type", "APPLICATION"),
new XElement("parm", new XAttribute("name", "APPID"), new XAttribute("value", "w5")),
new XElement("parm", new XAttribute("name", "TO-NAPID"), new XAttribute("value", "INTERNET")),
new XElement("parm", new XAttribute("name", "NAME"), new XAttribute("value", "SYNCSETTINGS")),
new XElement("parm", new XAttribute("name", "ADDR"), new XAttribute("value", "http://syncserver/sync")),
new XElement("characteristic", new XAttribute("type", "RESOURCE"),
new XElement("parm", new XAttribute("name", "URI"), new XAttribute("value", "pb")),
new XElement("parm", new XAttribute("name", "NAME"), new XAttribute("value", "Contacts DB")),
new XElement("parm", new XAttribute("name", "AACCEPT"), new XAttribute("value", "text/x-vcard"))
),
new XElement("characteristic", new XAttribute("type", "RESOURCE"),
new XElement("parm", new XAttribute("name", "URI"), new XAttribute("value", "cal")),
new XElement("parm", new XAttribute("name", "NAME"), new XAttribute("value", "Calendar DB")),
new XElement("parm", new XAttribute("name", "AACCEPT"), new XAttribute("value", "text/x-vcalendar"))
),
new XElement("characteristic", new XAttribute("type", "RESOURCE"),
new XElement("parm", new XAttribute("name", "URI"), new XAttribute("value", "notes")),
new XElement("parm", new XAttribute("name", "NAME"), new XAttribute("value", "Notes DB")),
new XElement("parm", new XAttribute("name", "AACCEPT"), new XAttribute("value", "text/plain"))
),
new XElement("characteristic", new XAttribute("type", "APPAUTH"),
new XElement("parm", new XAttribute("name", "AAUTHNAME"), new XAttribute("value", Username)),
new XElement("parm", new XAttribute("name", "AAUTHSECRET"), new XAttribute("value", Password))
)
)
)
);
ota.Save(Server.MapPath("~/OTA/") + Username + ".xml");
return (ota.ToString());
}
// Create the xml document containe
XmlDocument doc = new XmlDocument();// Create the XML Declaration, and append it to XML document
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", null, null);
doc.AppendChild(dec);// Create the root element
XmlElement root = doc.CreateElement("Library");
doc.AppendChild(root);
// Create Books
// Note that to set the text inside the element,
// you use .InnerText instead of .Value (which will throw an exception).
// You use SetAttribute to set attribute
XmlElement book = doc.CreateElement("Book");
book.SetAttribute("BookType", "Hardcover");
XmlElement title = doc.CreateElement("Title");
title.InnerText = "Door Number Three";
XmlElement author = doc.CreateElement("Author");
author.InnerText = "O'Leary, Patrick";
book.AppendChild(title);
book.AppendChild(author);
root.AppendChild(book);
book = doc.CreateElement("Book");
book.SetAttribute("BookType", "Paperback");
title = doc.CreateElement("Title");
title.InnerText = "Lord of Light";
author = doc.CreateElement("Author");
author.InnerText = "Zelanzy, Roger";
book.AppendChild(title);
book.AppendChild(author);
root.AppendChild(book);
string xmlOutput = doc.OuterXml;
The same code but using an XMLWriter to a memory stream.
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = true;
MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms, wSettings);// Write Declaration
xw.WriteStartDocument();
// Write the root node
xw.WriteStartElement("Library");
// Write the books and the book elements
xw.WriteStartElement("Book");
xw.WriteStartAttribute("BookType");
xw.WriteString("Hardback");
xw.WriteEndAttribute();
xw.WriteStartElement("Title");
xw.WriteString("Door Number Three");
xw.WriteEndElement();
xw.WriteStartElement("Author");
xw.WriteString("O'Leary, Patrick");
xw.WriteEndElement();
xw.WriteEndElement();
// Write another book
xw.WriteStartElement("Book");
xw.WriteStartAttribute("BookType");
xw.WriteString("Paperback");
xw.WriteEndAttribute();
xw.WriteStartElement("Title");
xw.WriteString("Lord of Light");
xw.WriteEndElement();
xw.WriteStartElement("Author");
xw.WriteString("Zelanzy, Roger");
xw.WriteEndElement();
xw.WriteEndElement();
// Close the document
xw.WriteEndDocument();
// Flush the write
xw.Flush();
Byte[] buffer = new Byte[ms.Length];
buffer = ms.ToArray();
string xmlOutput = System.Text.Encoding.UTF8.GetString(buffer);
0
If you don't want (or can't) to use LINQ to XML
, neither 3 to duplicate your Order
class to include XML 2 serialization, and thinks XmlWriter
is too much verbose, you 1 can go with plain classical XmlDocument
class:
// consider Order class that data structure you receive from your ERP system
List<Order> orders = YourERP.GetOrders();
XmlDocument xml = new XmlDocument();
xml.AppendChild(xml.CreateElement("Orders"));
foreach (Order order in orders)
{
XmlElement item = xml.CreateElement("Order");
item.SetAttribute("OrderNumber", order.OrderNumber);
item.AppendChild(xml.CreateElement("ItemNumber")).Value = order.ItemNumber;
item.AppendChild(xml.CreateElement("QTY" )).Value = order.Quantity;
item.AppendChild(xml.CreateElement("WareHouse" )).Value = order.WareHouse;
xml.DocumentElement.AppendChild(item);
}
There are a lot of good suggestions in this 5 thread, but one that hasn't been mentioned: Defining 4 an ADO DataSet
and serializing/deserializing using 3 the ReadXml
and WriteXml
methods. This can be a very simple 2 and attractive solution. Your XML isn't 1 in quite the right format, but it's close.
I found it largely depends how complex your 12 original data is.
If your data is nicely 11 organized in objects and it is sufficient 10 to dump it in XML, Linq is very verbose 9 and powerful. But as soon as there are object 8 inter-dependencies, I don't think you want 7 to go with the Linq one-liner, as this is 6 real pain to debug and/or extend.
For those 5 cases, I'd prefer to go with XmlDocument, create 4 a help method to facilitate adding attributes 3 to an element (see below), and use Linq 2 in foreach loops around the XML creation 1 blocks.
private void XAttr(ref XmlNode xn, string nodeName, string nodeValue)
{
XmlAttribute result = xn.OwnerDocument.CreateAttribute(nodeName);
result.InnerText = nodeValue;
xn.Attributes.Append(result);
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.