C# Linq XML InvalidOperationException - c#

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);
}

Related

Is there a faster way to generate XML Files

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.

How to add list, with LINQ, in an xml?

I have the following structure in an xml:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfNO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<No>
<Id>1</Id>
<Name>txt_1</Name>
<Text>some text</Text>
<Txt_relacionados>
<string>txt_4</string>
<string>txt_4</string>
<string>txt_4</string>
</Txt_relacionados>
<N>0</N>
<X>285</X>
<Y>31</Y>
<Externa>false</Externa>
<EM>false</EM>
<B>false</B>
<K>false</K>
<L1>false</L1>
<L2>false</L2>
<L3>false</L3>
<L4>false</L4>
<L5>false</L5>
<L6>false</L6>
<L7>false</L7>
<L8>false</L8>
<L9>false</L9>
<L10>false</L10>
<L11>false</L11>
<L12>false</L12>
<IP>0</IP>
</No>
I'm having trouble inserting the "Txt_relacionados" element because it contains a list of strings.
In the VO of this element i have the following:
[XmlArray("Txt_relacionados"), XmlArrayItem("string", Type = typeof(String))]
private List<String> txt_relacionados = new List<String>();
public List<String> Txt_relacionados
{
get { return txt_relacionados; }
set { txt_relacionados = value; }
}
What changes should I make in the code below to be able to insert this list?
public void inserir2(VoNo value)
{
RefreshXDoc();
XElement no = XElement.Load(path);
XElement element = new XElement("No",
new XElement("Id", value.Id),
new XElement("Name", value.Name),
new XElement("Text", value.Text),
new XmlArrayAttribute("Txt_relacionados", value.Txt_relacionados),
new XElement("N", value.N),
new XElement("X", value.X),
new XElement("Y", value.Y),
new XElement("Externa", value.Externa),
new XElement("B", value.B),
new XElement("K", value.K),
new XElement("L1", value.L1),
new XElement("L2", value.L2),
new XElement("L3", value.L3),
new XElement("L4", value.L4),
new XElement("L5", value.L5),
new XElement("L6", value.L6),
new XElement("L7", value.L7),
new XElement("L8", value.L8),
new XElement("L9", value.L9),
new XElement("L10", value.L10),
new XElement("L11", value.L11),
new XElement("L12", value.L12),
new XElement("IP", value.IP));
no.Add(element);
no.Save(path);
}
Thanks!!
You need to create Txt_relacionados element before you create new element No and then pass Txt_relacionados at proper place when you are creating No.
Consider following.
public void inserir2(VoNo value)
{
var rels = new XElement("Txt_relacionados");
foreach (var x in value.Txt_relacionados)
{
var elem = new XElement("string", x);
rels.Add(elem);
}
RefreshXDoc();
XElement no = XElement.Load(path);
XElement element = new XElement("No",
new XElement("Id", value.Id),
new XElement("Name", value.Name),
new XElement("Text", value.Text),
rels,
new XElement("N", value.N),
new XElement("X", value.X),
new XElement("Y", value.Y),
new XElement("Externa", value.Externa),
new XElement("B", value.B),
new XElement("K", value.K),
new XElement("L1", value.L1),
new XElement("L2", value.L2),
new XElement("L3", value.L3),
new XElement("L4", value.L4),
new XElement("L5", value.L5),
new XElement("L6", value.L6),
new XElement("L7", value.L7),
new XElement("L8", value.L8),
new XElement("L9", value.L9),
new XElement("L10", value.L10),
new XElement("L11", value.L11),
new XElement("L12", value.L12),
new XElement("IP", value.IP));
no.Add(element);
no.Save(path);
}

Nesting XML Tags using Linq with Data From DB

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;
}
}
}

Add collection of items( xml elements) to XML doc

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");

Build a Yaml document dynamically from c#

Is it possible to build a Yaml document dynamically from c# with Yaml.DotNet or another library?
I understand how this can be done using serialisation, however that requires starting with an object structure.
I'm looking to find a way to create the Yaml document nodes on the fly as you would with Xml using the XElement.Add(object) method for example.
You can do that using YamlDotNet. You start by creating a YamlStream, add one or more document to it, then you can add sequences, mappings and scalars to it.
Here is an example on how to do it:
var address = new YamlMappingNode(
new YamlScalarNode("street"),
new YamlScalarNode("123 Tornado Alley\nSuite 16") { Style = YamlDotNet.Core.ScalarStyle.Literal },
new YamlScalarNode("city"),
new YamlScalarNode("East Westville"),
new YamlScalarNode("state"),
new YamlScalarNode("KS")
) { Anchor = "main-address" };
var stream = new YamlStream(
new YamlDocument(
new YamlMappingNode(
new YamlScalarNode("repeipt"),
new YamlScalarNode("Oz-Ware Purchase Invoice"),
new YamlScalarNode("date"),
new YamlScalarNode("2007-08-06"),
new YamlScalarNode("customer"),
new YamlMappingNode(
new YamlScalarNode("given"),
new YamlScalarNode("Dorothy"),
new YamlScalarNode("family"),
new YamlScalarNode("Gale")
),
new YamlScalarNode("items"),
new YamlSequenceNode(
new YamlMappingNode(
new YamlScalarNode("part_no"),
new YamlScalarNode("A4786"),
new YamlScalarNode("descrip"),
new YamlScalarNode("Water Bucket (Filled)"),
new YamlScalarNode("price"),
new YamlScalarNode("1.47"),
new YamlScalarNode("quantity"),
new YamlScalarNode("4")
),
new YamlMappingNode(
new YamlScalarNode("part_no"),
new YamlScalarNode("E1628"),
new YamlScalarNode("descrip"),
new YamlScalarNode("High Heeled \"Ruby\" Slippers"),
new YamlScalarNode("price"),
new YamlScalarNode("100.27"),
new YamlScalarNode("quantity"),
new YamlScalarNode("1")
)
),
new YamlScalarNode("bill-to"), address,
new YamlScalarNode("ship-to"), address,
new YamlScalarNode("specialDelivery"),
new YamlScalarNode("Follow the Yellow Brick\n" +
"Road to the Emerald City.\n" +
"Pay no attention to the\n" +
"man behind the curtain.")
{
Style = YamlDotNet.Core.ScalarStyle.Literal
}
)
)
);
I've now worked out how to do this using Yaml.Net. The YamlStream needs to be loaded with some initial content using the Load() method.
const string initialContent = "---\nversion: 1\n...";
var sr = new StringReader(initialContent);
var stream = new YamlStream();
stream.Load(sr);
You can then cast the RootNode of the YamlDocument to YamlMappingNode which has an Add method.
var rootMappingNode = (YamlMappingNode)stream.Documents[0].RootNode;
rootMappingNode.Add("shout", "yay!");
You can then add a variety of node types before saving:
var props = new YamlMappingNode();
props.Add("prop1", "value1");
props.Add("prop2", "value2");
rootMappingNode.Add("itemWithProps", props);
var props2 = new YamlMappingNode();
props2.Add("prop1", "value1");
props2.Add("prop2", "value2");
var props3 = new YamlMappingNode();
props3.Add("prop1", "value1");
props3.Add("prop2", "value2");
var seq = new YamlSequenceNode();
seq.Add(props2);
seq.Add(props3);
rootMappingNode.Add("sequenceOfItems", seq);
var col = new YamlSequenceNode();
col.Style = SequenceStyle.Flow;
col.Add("a");
col.Add("b");
col.Add("c");
var seqMapping = new YamlMappingNode();
seqMapping.Add("collection", col);
seq.Add(seqMapping);
using (TextWriter writer = File.CreateText("C:\\temp\\test.yaml"))
stream.Save(writer, false);
The output from this example is:
version: 1
shout: yay!
itemWithProps:
prop1: value1
prop2: value2
sequenceOfItems:
- prop1: value1
prop2: value2
- prop1: value1
prop2: value2
- collection: [a, b, c]
...
Thanks to #Antoine Aubry for creating Yaml.Net and vaguely pointing me in right direction.

Categories