How to build an XDocument with a foreach and LINQ? - c#

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?

Related

Saving in XML only last object from the list

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

XElement with LINQ add node after specific node

I've got this XML:
<?xml version="1.0" encoding="utf-8"?>
<JMF SenderID="InkZone-Controller" Version="1.2">
<Command ID="cmd.00695" Type="Resource">
<ResourceCmdParams ResourceName="InkZoneProfile" JobID="K_41">
<InkZoneProfile ID="r0013" Class="Parameter" Locked="false" Status="Available" PartIDKeys="SignatureName SheetName Side Separation" DescriptiveName="Schieberwerte von DI" ZoneWidth="32">
<InkZoneProfile SignatureName="SIG1">
<InkZoneProfile Locked="False" SheetName="S1">
<InkZoneProfile Side="Front">
<ColorPool Class="Parameter" DescriptiveName="Colors for the job" Status="Available">
<InkZoneProfile Separation="PANTONE 647 C" ZoneSettingsX="0 0,003 " />
</ColorPool>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</ResourceCmdParams>
</Command>
</JMF>
I'm trying to add a node after a specific node() , using XElement and Linq. But my LINQ query always returns me null.
Tried this:
XElement InkZonePath = XmlDoc.Element("JMF").Elements("InkZoneProfile").Where(z => z.Element("InkZoneProfile").Attribute("Side").Value == "Front").SingleOrDefault();
And this:
XmlDoc.Element("JMF")
.Elements("InkZoneProfile").Where(InkZoneProfile => InkZoneProfile.Attribute("Side")
.Value == "Front").FirstOrDefault().AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
I've built this queries following those examples:
Select XElement where child element has a value
Insert XElements after a specific node
LINQ-to-XML XElement query NULL
But it didn't worked as expected. What is wrong with the LINQ Query ? From what i've read it should work (logically reading the expression i can understand it).
Thanks
EDIT-1: Entire writexml Method
public void writexml(xmldatalist XMLList, variables GlobalVars)
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false)
};
string DesktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string FileExtension = ".xml";
string PathString = Path.Combine(DesktopFolder, "XML");
System.IO.Directory.CreateDirectory(PathString);
foreach (List<xmldata> i in XMLList.XMLArrayList)
{
int m = 0;
foreach (var x in i)
{
string XMLFilename = System.IO.Path.GetFileNameWithoutExtension(x.xml_filename);
GlobalVars.FullPath = Path.Combine(PathString, XMLFilename + FileExtension);
if (!File.Exists(GlobalVars.FullPath))
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("JMF",
new XAttribute("SenderID", "InkZone-Controller"),
new XAttribute("Version", "1.2"),
new XElement("Command",
new XAttribute("ID", "cmd.00695"),
new XAttribute("Type", "Resource"),
new XElement("ResourceCmdParams",
new XAttribute("ResourceName", "InkZoneProfile"),
new XAttribute("JobID", "K_41"),
new XElement("InkZoneProfile",
new XAttribute("ID", "r0013"),
new XAttribute("Class", "Parameter"),
new XAttribute("Locked", "False"),
new XAttribute("Status", "Available"),
new XAttribute("PartIDKeys", "SignatureName SheetName Side Separation"),
new XAttribute("DescriptiveName", "Schieberwerte von DI"),
new XAttribute("ZoneWidth", "32"),
new XElement("InkZoneProfile",
new XAttribute("SignatureName", "SIG1"),
new XElement("InkZoneProfile",
new XAttribute("Locked", "false"),
new XAttribute("SheetName", "S1"),
new XElement("InkZoneProfile",
new XAttribute("Side", "Front"),
new XElement("ColorPoolClass",
new XAttribute("Class", "Parameter"),
new XAttribute("DescriptiveName", "Colors for the job"),
new XAttribute("Status", "Available")
)))))))));
doc.Save(GlobalVars.FullPath);
XDocument XmlDoc = new XDocument();
XmlDoc = XDocument.Load(GlobalVars.FullPath);
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
}
XmlDoc.Save(GlobalVars.FullPath);
}//Closing !FileExists
}//Closing inner foreach
}//Closing outer foreach
}//Closing writexml method
The problem with your current code is here : Element("JMF").Elements("InkZoneProfile") Since InkZoneProfile is not a direct child of JMF it will not return anything. Use Descendants instead.
Check difference between Elements & Descendants.
This should give you correct result:-
XElement InkZonePath = xdoc.Element("JMF").Descendants("InkZoneProfile")
.SingleOrDefault(z => (string)z.Attribute("Side") == "Front")
After this you can add whatever node you want to add using AddAfterSelf. Also note I have used SingleOrDefault here, but you may get exception if you have multiple matching nodes with this, In that case consider using FirstOrDefault.
Update:
To add new node:-
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", "Test"),
new XAttribute("ZoneSettingsX", "Test2")));
}
//Save XDocument
xdoc.Save(#"C:\Foo.xml");
You need to use Descendants method instead Elements:
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if(InkZonePath !=null)
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
you can use Descendants instead.
var node = XmlDoc.Descendants("InkZoneProfile").Where(x=> x.Attribute("Side") !=null && x.Attribute("Side").Value == "Front").FirstorDefault();

XDocument Error Nonwhitespace

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.

Writing Xml in Windows Phone Application

I have this code that work fine to create an xml document for my WPF application.
var doc = new XmlDocument();
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
var parentNode = doc.CreateElement("manga");
doc.AppendChild(parentNode);
foreach (var mList in mangaList)
{
var itemNode = doc.CreateElement("item");
var itemAttribute = doc.CreateAttribute("value");
itemAttribute.Value = mList.Key;
itemNode.InnerText = mList.Value;
itemNode.Attributes.Append(itemAttribute);
parentNode.AppendChild(itemNode);
}
var writer = new XmlTextWriter(#"Data\mangalist.xml", null);
writer.Formatting = Formatting.Indented;
doc.Save(writer);
writer.Close();
Now I want to create similar application for Windows Phone 7.5 and i'm stuck in porting above code to be able to run in WP. After quick searching i found that XmlDocument is not available in Windows Phone and have to switch using XDocument. I am far from familiar with XDocument and hope somebody can help to me make my windows phone apps outputting the same xml.
Thanks
Solution :
After good hints from #Pradeep Kesharwani and #dav_i I managed to port those codes above to use XDocument and StreamWriter instead of XmlDocument and XmlTextWriter which are not available for WP:
var doc = new XDocument(new XDeclaration("1.0", "utf-8", "no"));
var root = new XElement("manga");
var mangaList = new Dictionary<string, string>();
mangaList.Add("conan", "conan");
mangaList.Add("naruto", "naruto");
foreach (var mList in mangaList)
{
var itemNode = new XElement("item");
var itemAttribute = new XAttribute("value", mList.Key);
itemNode.Value = mList.Value;
itemNode.Add(itemAttribute);
root.Add(itemNode);
}
doc.Add(root);
using (var writer = new StreamWriter(#"Data\mangalist2.xml"))
{
writer.Write(doc.ToString());
}
As I said in comments, XDocument is pretty straight forward -
new XDocument(
new XDeclaration("1.0", "utf-8", "no"),
new XElement("root",
new XElement("something",
new XAttribute("attribute", "asdf"),
new XElement("value", 1234),
new XElement("value2", 4567)
),
new XElement("something",
new XAttribute("attribute", "asdf"),
new XElement("value", 1234),
new XElement("value2", 4567)
)
)
)
Gives the following
<root>
<something attribute="asdf">
<value>1234</value>
<value2>4567</value2>
</something>
<something attribute="asdf">
<value>1234</value>
<value2>4567</value2>
</something>
</root>
Hopefully this will help you!
To automatically populate in a loop, you could do something like this:
var somethings = new List<XElement>();
for (int i = 0; i < 3; i++)
somethings.Add(new XElement("something", new XAttribute("attribute", i + 1)));
var document = new XDocument(
new XElement("root",
somethings));
Which results in
<root>
<something attribute="1" />
<something attribute="2" />
<something attribute="3" />
</root>
This Create method could be used to create a xml doc in wp7
private void CreateXml()
{
string xmlStr = "<RootNode></RootNode>";
XDocument document = XDocument.Parse(xmlStr);
XElement ex = new XElement(new XElement("FirstNOde"));
XElement ex1 = new XElement(new XElement("second"));
ex1.Value = "fdfgf";
ex.Add(ex1);
document.Root.Add(new XElement("ChildNode", "World!"));
document.Root.Add(new XElement("ChildNode", "World!"));
document.Root.Add(ex);
string newXmlStr = document.ToString();
}
This will be the created xml
<RootNode>
<ChildNode>World!</ChildNode>
<ChildNode>World!</ChildNode>
<FirstNOde>
<second>fdfgf</second>
</FirstNOde>
</RootNode>
public void ReadXml()
{
IsolatedStorageFileStream isoFileStream = myIsolatedStorage.OpenFile("Your xml file name", FileMode.Open);
using (XmlReader reader = XmlReader.Create(isoFileStream))
{
XDocument xml = XDocument.Load(reader);
XElement root1 = xml.Root;
}
}

Add xml namespace to XDocument

the output the i need is
<ns1:collection>
<ns1:child1>value1</ns1:child3>
<ns1:child2>value2</ns1:child3>
<ns1:child3>value3</ns1:child3>
</ns1:collection>
i try to do it by the code below - but i get an exception -...
How to add the namespace on this one ??
XDocument xDoc = new XDocument( new XElement( "collection",
new XElement( "ns1:child1", value1 ),
new XElement( "ns1:child2", value2 ),
new XElement( "ns1:child3", value3 ) ) );
I also try to use the
XNamespace ns1 = "http://url/for/ns1";";
and to do
( ns1 + "child1", value1 )
And still nothing
Move namespace declaration to the fictional parent element:
XDocument xDoc = new XDocument(new XElement("root",
new XAttribute(XNamespace.Xmlns + "ns1", ns1),
new XElement(ns1 + "collection",
new XElement(ns1 + "child1", value1),
new XElement(ns1 + "child2", value2),
new XElement(ns1 + "child3", value3))));
XDocument xDoc = new XDocument(new XElement(ns1+"collection",
new XAttribute(XNamespace.Xmlns+"ns1",ns1),
new XElement(ns1+ "child1", value1),
new XElement(ns1+"child2", value2),
new XElement(ns1+"child3", value3)));

Categories

Resources