I am generating a XML file with hundreds of thousands of elements and the runtime of my code is currently over 3 minutes! I have tried both XDocument and XmlDocument to see if either would decrease the runtime; XDocument was better, but only by a small margin. Is there anything else I can try?
Code
string path = Server.MapPath("~/Temp/employee.xml");
XDocument d = new XDocument(new XElement("Employees"));
d.Declaration = new XDeclaration("1.0", "utf-8", "true");
while (reader.Read())
{
d.Root.Add(new XElement("Employee",
new XElement("LastName", reader["lastname"].ToString()),
new XElement("FirstName", reader["firstname"].ToString()),
new XElement("MiddleInitial", reader["middleini"].ToString()),
new XElement("ID", reader["id"].ToString()),
new XElement("Title", reader["title"].ToString()),
new XElement("DOB", reader["title"].ToString()),
new XElement("Category", reader["category"].ToString()),
new XElement("Supervisor", reader["supervisor"].ToString()),
new XElement("CurrentAddress", reader["address1"].ToString()),
new XElement("Address", reader["address2"].ToString()),
new XElement("City", reader["city"].ToString()),
new XElement("State", reader["state"].ToString()),
new XElement("ZipCode", reader["zip"].ToString()),
new XElement("OfficePhone", reader["office_phone"].ToString()),
new XElement("HomePhone", reader["home_phone"].ToString()),
new XElement("Email", reader["email_address"].ToString()),
new XElement("DateHired", reader["chem_employment_date"].ToString()),
new XElement("DateTerminated", reader["emp_terminate_date"].ToString()),
new XElement("StatusCode", reader["status_code"].ToString()),
new XElement("Room", reader["room"].ToString()),
new XElement("IsPrivate", reader["isprivate"].ToString()),
new XElement("Floor", reader["floor"].ToString()),
new XElement("Wing", reader["wing"].ToString()),
new XElement("InRoster", reader["isroster"].ToString()),
new XElement("RosterCategory", reader["roster_category"].ToString()),
new XElement("LastModified", reader["lastmodified"].ToString()),
new XElement("ShowReport", reader["isbudget"].ToString()),
new XElement("ModifiedBy", reader["lastmodifiedby"].ToString())
));
d.Save(path);
}
Try using the xmlwriter class
https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlwriter?view=net-5.0
C# export of large datatbase to XML
XmlWriter uses less memory than XmlDocument, but you will have to write the entire document from scratch each time.
Related
I am trying to create an XML file based on a .csv file. I figured I could use my old method for reading the .csv file and just replace some parts. However I get an exception saying:
InvalidOperationException
Here is my code:
public void LoadFile() {
XDocument outputDocument = new XDocument();
outputDocument.Add(new XElement("Inventory"));
using (var fstream = File.OpenRead(FilePathProducts))
using (var fileReader = new StreamReader(fstream)) {
while (!fileReader.EndOfStream) {
var readLine = fileReader.ReadLine();
var words = readLine.Split(';');
outputDocument.Add(
new XElement("Item",
new XElement("Name", words[1]),
new XElement("Count", words[3]),
new XElement("Price", words[4]),
new XElement("Comment", "<Missing Value>"),
new XElement("Artist", "<Missing Value>"),
new XElement("Publisher", "Nintendo"),
new XElement("Genre", "<Missing Value>"),
new XElement("Year", words[0]),
new XElement("ProductID", words[2])
)
);
}
}
outputDocument.Save(FilePathResult);
}
It says something about "a wrongfully structured document is created" (roughly translated.)
When I tried removing the loop, the output file only displayed a self-closing Inventory-tag. I expected it to be an open and close -tag. So I'm guessing something is wrong with line 3 of the code above.
You are trying to append your Item elements directly to the XML document and XML documents are not allowed to have more than one root element. That is why you are getting an error.
I presume you actually want to add them to the Inventory element, in which case you should do this:
public void LoadFile() {
var outputDocument = new XDocument();
var inventory = new XElement("Inventory");
outputDocument.Add(inventory);
using (var fstream = File.OpenRead(FilePathProducts))
using (var fileReader = new StreamReader(fstream)) {
while (!fileReader.EndOfStream) {
var readLine = fileReader.ReadLine();
var words = readLine.Split(';');
inventory.Add(
new XElement("Item",
new XElement("Name", words[1]),
new XElement("Count", words[3]),
new XElement("Price", words[4]),
new XElement("Comment", "<Missing Value>"),
new XElement("Artist", "<Missing Value>"),
new XElement("Publisher", "Nintendo"),
new XElement("Genre", "<Missing Value>"),
new XElement("Year", words[0]),
new XElement("ProductID", words[2])
)
);
}
}
outputDocument.Save(FilePathResult);
}
I am trying to build an XML document using Linq, XElement and data from Database,
It's working kinda, but in my XML, I want to close the tag and start a new tag and get the results from the query to populate into for the Tag, it is complaining that my variable r in the tag is unresolved, how can I make this work, or is there a better way of building the XML. All the child elements should be under the parent , having two children and , which has their own set of children.
Here is the code below
public void GenerateXML(int id, string site, string state, string country, string bFn, string bLn, string sFn, string sLn)
{
var results = (from o in _db.Orders
where o.OrderId == id
select o).ToList();
var xmlDoc = new XElement("Order",
from r in results
select
new XElement("OrderHeader",
new XElement("SiteId", site),
new XElement("OrderId", r.OrderId),
new XElement("Time", r.OrderDate.Value),
new XElement("Subtotal", r.SubTotal),
new XElement("Shipping", ""),
new XElement("SalesTax", r.SalesTax),
new XElement("Total", r.Total),
new XElement("PaymentAmount", ""),
new XElement("PaymentMethod", ""),
new XElement("ArchiTypeAcctNum", "20001"),
new XElement("TaxExempt", r.TaxExempt),
new XElement("SpecialInstructions", r.SpecialInstructions),
new XElement("BillTo",
new XElement("BillEmail", r.BillToEmail),
new XElement("FirstName", bFn),
new XElement("LastName", bLn),
new XElement("CompanyName", r.BillCompany),
new XElement("Address1", r.BillToAddress),
new XElement("City", r.BillToCity),
new XElement("State", state),
new XElement("Country", country),
new XElement("Zip", r.BillToZip),
new XElement("Phone", r.BillToPhoneNumber)),
new XElement("ShipTo",
new XElement("FirstName", sFn),
new XElement("LastName", sLn),
new XElement("CompanyName", r.ShipCompany),
new XElement("Address1", r.ShipToAddress),
new XElement("City", r.ShipToCity),
new XElement("State", state),
new XElement("Country", country),
new XElement("Zip", r.ShipToZip),
new XElement("Phone", r.ShipToPhoneNumber))),
new XElement("Items",
from i in r.Items
select new XElement("Item",
new XElement("SKU", i.SkuNumber),
new XElement("PROD_Name", i.ProductName),
new XElement("Description", i.Description),
new XElement("Attributes", i.Attributes),
new XElement("Quantity", i.Quantity),
new XElement("UnitPrice", i.UnitPrice),
new XElement("InkColor", i.InkColor)))
);
xmlDoc.Save(Server.MapPath(#"~/Xml/Orders.xml"));
RedirectToAction("Save");
}
I wrote an extension for same purpose. I think much easier . you can just use as orders.EntityToXml();
public static class XmlExtensions
{
public static bool EntityToXml<T>(this T entity, string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentNullException(nameof(filePath));
}
var dir = Path.GetDirectoryName(filePath);
if (string.IsNullOrEmpty(dir))
{
throw new ArgumentNullException(nameof(filePath));
}
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var serializer= new System.Xml.Serialization.XmlSerializer(typeof(T));
using (var stream = new StreamWriter(filePath))
{
serializer.Serialize(stream , entity);
return true;
}
}
}
How to add the collection of items to XML Document ? Something I started trying, but it's wrong! I'll be grateful for the link on the Internet at a good tutorial too. Also, It is assumed that the iteration will be some elements.
Such code:
public static void Create_Interfaces()
{
XDocument Interfaces;
Interfaces = XDocument.Load("Interfaces.xml");
List<string> intf = new List<string>{"em0","em1","em2"};
foreach (var i in intf)
{
Interfaces = new XDocument(
new XElement("Interfaces",
new XElement("Interface",
new XElement("name", i),
new XElement("vlan-tagging", XElement.EmptySequence),
new XElement("unit",
new XElement("vlan-id", "10"),
new XElement("family", new XElement("inet", new XElement("address", new XElement("name", "10.10.1.23/24"))))))));
}
Interfaces.Save("Interfaces.xml");
}
Get your root element add new elements to it then save it:
var rootElement = Interfaces.Root;
foreach (var i in intf)
{
var element = new XElement("Interface",
new XElement("name", i),
new XElement("vlan-tagging", XElement.EmptySequence),
new XElement("unit",
new XElement("vlan-id", "10"),
new XElement("family",
new XElement("inet",
new XElement("address",
new XElement("name", "10.10.1.23/24"))))));
rootElement.Add(element);
}
rootElement.Save("Interfaces.xml");
I am new to XML and tried the following but I'm getting an exception. Can someone help me?
The exception is This operation would create an incorrectly structured document
My code:
string strPath = Server.MapPath("sample.xml");
XDocument doc;
if (!System.IO.File.Exists(strPath))
{
doc = new XDocument(
new XElement("Employees",
new XElement("Employee",
new XAttribute("id", 1),
new XElement("EmpName", "XYZ"))),
new XElement("Departments",
new XElement("Department",
new XAttribute("id", 1),
new XElement("DeptName", "CS"))));
doc.Save(strPath);
}
Xml document must have only one root element. But you are trying to add both Departments and Employees nodes at root level. Add some root node to fix this:
doc = new XDocument(
new XElement("RootName",
new XElement("Employees",
new XElement("Employee",
new XAttribute("id", 1),
new XElement("EmpName", "XYZ"))),
new XElement("Departments",
new XElement("Department",
new XAttribute("id", 1),
new XElement("DeptName", "CS"))))
);
You need to add root element.
doc = new XDocument(new XElement("Document"));
doc.Root.Add(
new XElement("Employees",
new XElement("Employee",
new XAttribute("id", 1),
new XElement("EmpName", "XYZ")),
new XElement("Departments",
new XElement("Department",
new XAttribute("id", 1),
new XElement("DeptName", "CS")))));
In my case I was trying to add more than one XElement to xDocument which throw this exception. Please see below for my correct code which solved my issue
string distributorInfo = string.Empty;
XDocument distributors = new XDocument();
XElement rootElement = new XElement("Distributors");
XElement distributor = null;
XAttribute id = null;
distributor = new XElement("Distributor");
id = new XAttribute("Id", "12345678");
distributor.Add(id);
rootElement.Add(distributor);
distributor = new XElement("Distributor");
id = new XAttribute("Id", "22222222");
distributor.Add(id);
rootElement.Add(distributor);
distributors.Add(rootElement);
distributorInfo = distributors.ToString();
Please see below for what I get in distributorInfo
<Distributors>
<Distributor Id="12345678" />
<Distributor Id="22222222" />
</Distributors>
I'm trying to build an XML file that reads the content to put inside from a list:
List<Snack> trashFoods
When I create the XML file, it would be something like this:
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment(""),
new XElement("Snacks",
trashFoods.Select(snack =>
new XElement("Type", snack.Type),
new XElement("Name", snack.Name)
),
)
What I want to, and I am unable to do is something like putting conditionals inside the construction of the XML file. Similar to this:
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment(""),
new XElement("Snacks",
trashFoods.Select(snack =>
new XElement("Type", snack.Type),
if(snack.Type == "Doritos")
{
new XElement("GotSauce", snack.Sauce),
}
new XElement("Name", snack.Name)
),
)
Is this such a thing even possible with LINQ to XML even if the syntax is not the same?
LINQ to XML objects are not immutable, so you can rewrite your declarative code into imperative one using Add():
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("")
);
XElement snacks = new XElement("Snacks");
foreach (var snack in trashFoods)
{
snacks.Add(new XElement("Type", snack.Type));
if(snack.Type == "Doritos")
snacks.Add(new XElement("GotSauce", snack.Sauce));
snacks.Add(new XElement("Name", snack.Name));
}
doc.Add(snacks);
It's not very pretty, but it work. Another approach would be to extract the conditional code into a method using yield return:
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment(""),
new XElement("Snacks",
trashFoods.Select(snack => GetSnackChildren(snack))));
…
IEnumerable<XElement> GetSnackChildren(Snack snack)
{
yield return new XElement("Type", snack.Type);
if(snack.Type == "Doritos")
{
yield return new XElement("GotSauce", snack.Sauce);
}
yield return new XElement("Name", snack.Name);
}
This looks better, but it splits code that probably should be together.
You could try this (which will always have a GotSauce element):
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment(""),
new XElement("Snacks",
trashFoods.Select(snack =>
new XElement("Snack",
new XElement("Type", snack.Type),
new XElement("Name", snack.Name),
new XElement("GotSauce", snack.Type=="Doritos" ? snack.Sauce : "")
)
)
));
or this (which won't)
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment(""),
new XElement("Snacks",
trashFoods.Select(snack =>
snack.Type=="Doritos" ?
new XElement("Snack",
new XElement("Type", snack.Type),
new XElement("Name", snack.Name),
new XElement("GotSauce", snack.Sauce)) :
new XElement("Snack",
new XElement("Type", snack.Type),
new XElement("Name", snack.Name))
)
)
);
or this (which doesn't either)
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment(""),
new XElement("Snacks",
trashFoods.Select(snack =>
new XElement("Snack",
new XElement("Type", snack.Type),
new XElement("Name", snack.Name),
snack.Type=="Doritos" ? new XElement("GotSauce", snack.Sauce) : null
)
)
));