I'm getting an error when trying to build an XDocument. The error is happening inside System.XML.Linq.Xdocument under the code
internal override void ValidateString(string s) {
if (!IsWhitespace(s)) throw new ArgumentException(Res.GetString(Res.Argument_AddNonWhitespace));
}
This code is generating a Null Reference Exception. Below is MY code for the XDocument, I'm lost as to where something I'm doing is causing this.
XDocument folderviewContents = new XDocument(
new XDeclaration("1.0", "utf8", "yes"),
new XElement("LVNPImport",
new XAttribute("xmlns" + "xsd", XNamespace.Get("http://www.w3.org/2001/XMLSchema")),
new XAttribute("xmlns" + "xsi", XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance"))),
new XElement("InterfaceIdentifier", "835"),
//Start of FolderPaths
new XElement("FolderPaths",
new XElement("Folder",
new XAttribute("fromDate", "TEST"),
//attributes for Folder w/ lots of attributes
new XAttribute("toDate", "TEST"),
new XAttribute("contactName", "APerson"),
new XAttribute("email", "AnEmail"),
//value for that long Folder w/ lots of attributes
"Remittance Advice"),
//Facility
new XElement("Folder", "TEST"),
//PayorID
new XElement("Folder", "TEST"),
//RemitDate Year
new XElement("Folder","TEST"),
//RemitDate Month/Year
new XElement("Folder","TEST")),
new XElement("DocumentType", "RA"),
new XElement("DocumentDescription","TEST"),
new XElement("TotalFiles", "1"));
//Create a writer to write XML to the console.
XmlTextWriter writer = null;
writer = new XmlTextWriter(Console.Out);
//Use indentation for readability.
writer.Formatting = Formatting.Indented;
writer.Indentation = 4;
folderviewContents.WriteTo(writer);
writer.WriteEndDocument();
writer.Close();
Console.ReadLine();
Edit
Updated code
You were creating more than one element at root level. Assuming LVNPImport is your root node, just moving one closing bracket fixes this:
XDocument folderviewContents = new XDocument(
new XDeclaration("1.0", "utf8", "yes"),
new XElement("LVNPImport",
new XAttribute("xmlns" + "xsd", XNamespace.Get("http://www.w3.org/2001/XMLSchema")),
new XAttribute("xmlns" + "xsi", XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance")),
new XElement("InterfaceIdentifier", "835"),
//Start of FolderPaths
new XElement("FolderPaths",
new XElement("Folder",
new XAttribute("fromDate", "TEST"),
//attributes for Folder w/ lots of attributes
new XAttribute("toDate", "TEST"),
new XAttribute("contactName", "APerson"),
new XAttribute("email", "AnEmail"),
//value for that long Folder w/ lots of attributes
"Remittance Advice"),
//Facility
new XElement("Folder", "TEST"),
//PayorID
new XElement("Folder", "TEST"),
//RemitDate Year
new XElement("Folder", "TEST"),
//RemitDate Month/Year
new XElement("Folder", "TEST")),
new XElement("DocumentType", "RA"),
new XElement("DocumentDescription", "TEST"),
new XElement("TotalFiles", "1")));
I have tested this locally, and the XDocument is created without errors.
Related
I'll try explain what i'm trying to achieve as best I can...
I'm generating order XML document in C# using XDocument based on data from a SQL Query. Some of the XML Elements & Attributes need only appear once but the Order Line information needs to be looped a number of times; for example if an order contains 3 items I need to generate three instances of the Order Line information, if an order contains 5 items I need to generate five instances of the Order Line information etc.
My XML structure will look like below:
Delivery information - no loop required
Invoice information - no loop required
Order Line information - loop required as per above
More delivery information (shipping type) - no loop required
To get the static information, i'm using a SQLDataReader and passing the information into a variable:
var example = reader["column_name"];
This works fine for when I don't need to loop. However i'm now at the stage where I want to create the loop for Order Line information.
Each Order Line will come as a seperate row in my SQL query, the row will contain all the static information (Delivery information, invoice information etc) as well as the individual Order Line information (SKU, Product Description etc).
Essentially what I think I need to do is a foreach loop for each row in the SQLDataReader, and populate variables to drop into the XML OrderLine section, however I'm not sure how to do this, nor is this the best approach.
I can create a static version of the OrderLine section easily enough, but how would I go about looping through the SQL rows to pass variables into it? My static OrderLine code is as below
new XElement("OrderLine", new XAttribute("TypeDescription", "Goods & Services"), new XAttribute("Action", "Add"), new XAttribute("TypeCode", "GDS"),
new XElement("LineNumber", new XAttribute("Preserve", "true"), "item_number then increment by 1"),
new XElement("Product",
new XElement("SuppliersProductCode", "VALUE"),
new XElement("Description", "VALUE")),
new XElement("Quantity", new XAttribute("UOMDescription", "VALUE"), new XAttribute("UOMCode", "VALUE"),
new XElement("Amount", "Value")),
new XElement("Quantity", new XAttribute("UOMDescription", "VALUE"), new XAttribute("UOMCode", "VALUE"),
new XElement("UnitPrice", "Value"))),
My full XML generate code looks like this:
new XElement("Order",
new XElement("OrderHead",
new XElement("Schema",
new XElement("Value", "3.05")),
new XElement("Parameters",
new XElement("Language", "en-GB"),
new XElement("DecimalSeperator", "."),
new XElement("Precision", "12.3")),
new XElement("OrderType", new XAttribute("Code", "PUO"), "Purchase Order"),
new XElement("OrderCurrency",
new XElement("Currency", new XAttribute("Code", "GBP"), "GB Pounds Sterling"))),
new XElement("OrderReferences",
new XElement("BuyersOrderNumber", new XAttribute("Preserve", "true"), incrementid_var)),
new XElement("Buyer",
new XElement("BuyerReferences",
new XElement("SupplierscodeForBuyer", "****VALUE****")),
new XElement("Contact",
new XElement("Name"))),
new XElement("Delivery",
new XElement("DeliverTo",
new XElement("Party", firstname_var + " " + lastname_var),
new XElement("Contact",
new XElement("Name", firstname_var + " " + lastname_var),
new XElement("Email", "****EMAIL ADDRESS****")),
new XElement("Address",
new XElement("AddressLine", street_var),
new XElement("AddressLine", city_var),
new XElement("AddressLine", region_var),
new XElement("PostCode", postcode_var),
new XElement("Country", new XAttribute("Code", countryid_var)))),
new XElement("DeliverFrom",
new XElement("Party", firstname_var + " " + lastname_var))),
new XElement("InvoiceTo",
new XElement("Party", firstname_var + " " + lastname_var),
new XElement("Address",
new XElement("AddressLine", street_var),
new XElement("AddressLine", city_var),
new XElement("AddressLine", region_var),
new XElement("PostCode", postcode_var),
new XElement("Country", new XAttribute("Code", countryid_var)))),
//Start Loop
new XElement("OrderLine", new XAttribute("TypeDescription", "Goods & Services"), new XAttribute("Action", "Add"), new XAttribute("TypeCode", "GDS"),
new XElement("LineNumber", new XAttribute("Preserve", "true"), "item_number then increment by 1"),
new XElement("Product",
new XElement("SuppliersProductCode", "VALUE"),
new XElement("Description", "VALUE")),
new XElement("Quantity", new XAttribute("UOMDescription", "VALUE"), new XAttribute("UOMCode", "VALUE"),
new XElement("Amount", "Value")),
new XElement("Quantity", new XAttribute("UOMDescription", "VALUE"), new XAttribute("UOMCode", "VALUE"),
new XElement("UnitPrice", "Value"))),
// End Loop
//Start Delivery Information
new XElement("OrderLine", new XAttribute("TypeDescription", "VALUE"), new XAttribute("Action", "VALUE"), new XAttribute("TypeCode", "VALUE"),
new XElement("LineNumber", new XAttribute("Preserve", "true"), "item_number + 1"),
new XElement("Product",
new XElement("SuppliersProductCode", "**** DELIVERY CODE ****"),
new XElement("Description", "**** DELIVERY DESCRIPTION****")),
new XElement("Quantity", new XAttribute("UOMDescription", "Piece"), new XAttribute("UOMCode", "PCE"),
new XElement("Amount", "1")),
new XElement("Quantity", new XAttribute("UOMDescription", "Pack"), new XAttribute("UOMCode", "PCK"),
new XElement("UnitPrice", "0"))),
new XElement("AdditionalOrderReferences",
new XElement("OrderReference", incrementid_var)),
new XElement("DeliveryInformation", "TELEPHONE NUMBER")));
Everything outside of the Start Loop, End Loop comments only needs to be generated once. If the order has 5 items I need to generate the code within those comments 5 times.
Any help is much appreciated as i'm going round in circles reading articles and still not sure how to implement this.
Thanks
Iain
Could you please tell me how can I fix my code? I tried a lot of ways, for example with XmlSerializer, but still nothing.
The code always saving the last item of the list, and I have no idea how to fix it.
The code:
foreach (ObservableCollection<Person> x in list)
{
XDocument xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Person",
from person in x
select new XElement("Person",
new XElement("Name", person.Name),
new XElement("Surname", person.Surname),
new XElement("Age", person.Age))));
xdoc.Save(path);
}
I would be greatful for any tip!
from the comments I understood you have a list of lists and you want to flatten it in the resulting XML. You could use this:
XDocument xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Persons",
from x in list
from person in x
select new XElement("Person",
new XElement("Name", person.Name),
new XElement("Surname", person.Surname),
new XElement("Age", person.Age))));
xdoc.Save("tmp.xml");
Your solution did not work because you saved the XML document in each iteration of the foreach loop, which would overwrite the existing file, so the result would have been only the last iteration of the loop
He is a slightly different xml linq solution :
XDocument xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("People"));
XElement people = xdoc.Root;
foreach (ObservableCollection<Person> x in list)
{
XElement person = new XElement("Person", fom person in x
select new XElement("Person",
new XElement("Name", person.Name),
new XElement("Surname", person.Surname),
new XElement("Age", person.Age)));
people.Add(person);
}
xdoc.Save(path);
I would like to generate an XDocument from my table (the structure is different) but I keep getting the following error :
LINQ to Entities does not recognize the method 'System.String ToString()' method.
I know this is caused by the Birthdate and I would need to use SqlFunctions.StringConvert but I'm working with Framework 4.0
Any ideas on how to solve that?
// Newsletter subscriptions
var news = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("USER",
from n in _dc.Newsletter_Datas
where n.Timestamp >= startDate
select
new XElement("ROW",
new XElement("UTI", n.ID),
new XElement("FIRSTNAME", n.FirstName),
new XElement("FAMILYNAME", n.LastName),
new XElement("GENDER", n.Gender),
new XElement("BIRTHDATE", n.BirthDate != null ? n.BirthDate.Value.ToString("yyyy-MM-dd") : "2003-01-01"),
new XElement("LANGUAGE", n.Language),
new XElement("EMAIL", n.Email),
new XElement("STREETNAME", n.StreetName),
new XElement("HOUSENR", n.HouseNr),
new XElement("BOXNR", n.BoxNr),
new XElement("ZIPCODE", n.Zipcode),
new XElement("CITY", n.City),
new XElement("COUNTRY", n.Country),
new XElement("TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("MESSAGE_ID", "SCNEWS02"),
new XElement("OPT_INS",
new XElement("OPT_IN",
new XElement("OPT_IN_CBP", "01000000000"),
new XElement("OPT_IN_INSERT_TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("OPT_IN_METHOD", "1"),
new XElement("OPT_IN_TYPE", n.OptIn),
new XElement("OPT_IN_CHANNEL", "2"))))));
Thank you very much
Break your query into separate parts, the first part is the query itself. That will be handled by the entity framework. Then cast that query to an IEnumerable<NewsletterData> to finish it off using LINQ to objects. Entity Framework can't do more than a simple projection (which you are clearly not doing).
var query =
from n in _dc.Newsletter_Datas
where n.Timestamp >= startDate
select n;
var news = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("USER",
from n in query.AsEnumerable() // LINQ to objects
select new XElement("ROW",
new XElement("UTI", n.ID),
new XElement("FIRSTNAME", n.FirstName),
new XElement("FAMILYNAME", n.LastName),
new XElement("GENDER", n.Gender),
new XElement("BIRTHDATE", n.BirthDate != null ? n.BirthDate.Value.ToString("yyyy-MM-dd") : "2003-01-01"),
new XElement("LANGUAGE", n.Language),
new XElement("EMAIL", n.Email),
new XElement("STREETNAME", n.StreetName),
new XElement("HOUSENR", n.HouseNr),
new XElement("BOXNR", n.BoxNr),
new XElement("ZIPCODE", n.Zipcode),
new XElement("CITY", n.City),
new XElement("COUNTRY", n.Country),
new XElement("TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("MESSAGE_ID", "SCNEWS02"),
new XElement("OPT_INS",
new XElement("OPT_IN",
new XElement("OPT_IN_CBP", "01000000000"),
new XElement("OPT_IN_INSERT_TS", n.Timestamp.ToString("yyyy-MM-dd hh:mm:ss")),
new XElement("OPT_IN_METHOD", "1"),
new XElement("OPT_IN_TYPE", n.OptIn),
new XElement("OPT_IN_CHANNEL", "2")
)
)
)
)
);
I have my linq code formatted like:
<Deck>
<Treasure>
<card>
.....
</card>
......
</treasure>
<Door>
<card>
.....
</card>
......
</Door>
In the following code how do I add another Door that is the same "level" as treasure? Everything I have tried keeps adding it as the same level as card. Here is what I have:
public void SaveXml(string path)
{
XElement xml;
XElement root = new XElement("Treasure");
foreach (var item in TreasureCards)
{
xml = new XElement("Card",
new XAttribute("name", item.Name),
new XElement("Type", item.Type),
new XElement("Image",
new XAttribute("path", item.Image)),
new XElement("Usage", item.Usage),
new XElement("Quantity", item.Quantity),
new XElement("Sell", item.Sell)
);
root.Add(xml);
}
root.Add(new XElement("Door"));
foreach (var item in DoorCards)
{
xml = new XElement("Card",
new XAttribute("name", item.Name),
new XElement("Type", item.Type),
new XElement("Image",
new XAttribute("path", item.Image)),
new XElement("Usage", item.Usage),
new XElement("Quantity", item.Quantity));
root.Add(xml);
}
You need to create the Deck element first:
XElement deck = new XElement("Deck");
Then add both the treasure (which i've taken the liberty of renaming from root to treasure) and the door to it:
XElement treasure = new XElement("Treasure")
...
deck.Add(treasure)
...
XElement door = new XElement("Door")
...
deck.Add(door)
I can use XDocument to build the following file which works fine:
XDocument xdoc = new XDocument
(
new XDeclaration("1.0", "utf-8", null),
new XElement(_pluralCamelNotation,
new XElement(_singularCamelNotation,
new XElement("id", "1"),
new XElement("whenCreated", "2008-12-31")
),
new XElement(_singularCamelNotation,
new XElement("id", "2"),
new XElement("whenCreated", "2008-12-31")
)
)
);
However, I need to build the XML file by iterating through a collection like this:
XDocument xdoc = new XDocument
(
new XDeclaration("1.0", "utf-8", null));
foreach (DataType dataType in _dataTypes)
{
XElement xelement = new XElement(_pluralCamelNotation,
new XElement(_singularCamelNotation,
new XElement("id", "1"),
new XElement("whenCreated", "2008-12-31")
));
xdoc.AddInterally(xelement); //PSEUDO-CODE
}
There is Add, AddFirst, AddAfterSelf, AddBeforeSelf, but I could get none of them to work in this context.
Is an iteration with LINQ like this possible?
Answer:
I took Jimmy's code suggestion with the root tag, changed it up a bit and it was exactly what I was looking for:
var xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement(_pluralCamelNotation,
_dataTypes.Select(datatype => new XElement(_singularCamelNotation,
new XElement("id", "1"),
new XElement("whenCreated", "2008-12-31")
))
)
);
Marc Gravell posted a better answer to this on this StackOverflow question.
You need a root element.
var xdoc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement("Root",
_dataTypes.Select(datatype => new XElement(datatype._pluralCamelNotation,
new XElement(datatype._singlarCamelNotation),
new XElement("id", "1"),
new XElement("whenCreated", "2008-12-31")
))
)
);
If I'm not mistaken, you should be able to use XDocument.Add():
XDocument xdoc = new XDocument
(
new XDeclaration("1.0", "utf-8", null));
foreach (DataType dataType in _dataTypes)
{
XElement xelement = new XElement(_pluralCamelNotation,
new XElement(_singularCamelNotation,
new XElement("id", "1"),
new XElement("whenCreated", "2008-12-31")
));
xdoc.Add(xelement);
}
I know it's very very old post but I stumbled across this today trying to solve the same problem. You have to add the element to the Root of the document:
xdoc.Root.Add(xelement);
What's wrong with the simple Add method?