How to generate an XML file dynamically using XDocument? - c#

As I wrote in the subject itself , how can I do that?
Note that solution like this are not appropriate as I want to create child nodes dynamically through running..
new XDocument(
new XElement("root",
new XElement("someNode", "someValue")
)
)
.Save("foo.xml");
I guess this was clear enough the first time but I will write it again:
I need to be able to add child nodes to given parent node while running, in the current syntax I've written this is static generated xml which doesn't contribute me at all because all is known in advance, which is not as my case.
How would you do it with Xdocument, is there away?

If a document has a defined structure and should be filled with dynamic data, you can go like this:
// Setup base structure:
var doc = new XDocument(root);
var root = new XElement("items");
doc.Add(root);
// Retrieve some runtime data:
var data = new[] { 1, 2, 3, 4, 5 };
// Generate the rest of the document based on runtime data:
root.Add(data.Select(x => new XElement("item", x)));

Very simple
Please update your code accordingly
XmlDocument xml = new XmlDocument();
XmlElement root = xml.CreateElement("children");
xml.AppendChild(root);
XmlComment comment = xml.CreateComment("Children below...");
root.AppendChild(comment);
for(int i = 1; i < 10; i++)
{
XmlElement child = xml.CreateElement("child");
child.SetAttribute("age", i.ToString());
root.AppendChild(child);
}
string s = xml.OuterXml;

Related

Adding child nodes to an XElement

I am trying to append objects into an XML file. The problem I currently have is it appends everything at the first level itself. I am trying to have the list as the parent element and list items as the child elements.
What I've tried: I came across a few posts where they use loops but I am unable to relate that to my context and code.
Code:
XDocument xDocument = XDocument.Load(#"C:\Users\hci\Desktop\Nazish\TangramsTool\TangramsTool\patterndata.xml");
XElement root = xDocument.Element("Patterns");
foreach (Pattern currentPattern in PatternDictionary.Values)
{
String filePath = currentPattern.Name.ToString();
IEnumerable<XElement> rows = root.Descendants("Pattern"); // Returns a collection of the descendant elements for this document or element, in document order.
XElement firstRow = rows.First(); // Returns the first element of a sequence.
if (currentPattern.PatternDistancesList.Count() == 9)
{
firstRow.AddBeforeSelf( //Adds the specified content immediately before this node.
new XElement("Pattern"),
new XElement("Name", filePath.Substring(64)),
new XElement("PatternDistancesList"),
new XElement("PatternDistance", currentPattern.PatternDistancesList[0].ToString()),
new XElement("PatternDistance", currentPattern.PatternDistancesList[1].ToString()),
}
}
Current XML File:
<Pattern/>
<Name>match.jpg</Name>
<PatternDistancesList/>
<PatternDistance>278</PatternDistance>
<PatternDistance>380</PatternDistance>
What I would like as the end result:
<Pattern>
<Name>match.jpg</Name>
<PatternDistancesList>
<PatternDistance>278</PatternDistance>
<PatternDistance>380</PatternDistance>
</PatternDistancesList>
<Pattern/>
Any tips will be much appreciated. I'm new to WPF and C# so still trying to learn things.
This should do the trick:
firstRow.AddBeforeSelf(
new XElement("Pattern",
new XElement("Name", filePath.Substring(64)),
new XElement("PatternDistancesList",
new XElement("PatternDistance", currentPattern.PatternDistancesList[0].ToString()),
new XElement("PatternDistance", currentPattern.PatternDistancesList[1].ToString()))));

How to modify Custom XML parts in Excel 2010/2013

I am trying to figure out how to modify custom XML parts previusly saved in Excel. All the web resources I have found so far explain how to add custom XML parts in Excel. This I already know. But I want to modify existing parts.
The API seems to have only Add method. If Add method is called again it adds additional XML parts.
I use the following code to save my custom XML
XNamespace NS = "http://schema.blabla.com";
var xDoc = new XDocument(
new XDeclaration("1.0", "utf-8", "no"),
new XComment("Custom XML Parts demo"),
new XElement(NS + "demo",
new XElement(NS + "config",
new XElement(NS + "property",
new XAttribute("value", "myVlaue",
new XAttribute("key", "myKey"))))));
Office.CustomXMLPart customXMLPart = workbook.CustomXMLParts.Add(xDoc.ToString(), System.Type.Missing);
I use the following code to retrieve my custom XML
var retrievedXMLParts = workbook.CustomXMLParts.SelectByNamespace(NS.NamespaceName);
//FirstOrDefault always returns first saved data, LastOrDefault needs to be called to get the latest
//var customXMLPart = retrievedXMLParts.Cast<CustomXMLPart>().FirstOrDefault();
var customXMLPart = retrievedXMLParts.Cast<CustomXMLPart>().LastOrDefault();
var propertiesXML = customXMLPart != null ? customXMLPart.XML : String.Empty;
What I would like to achieve is to check if a custom XML exists update its content instead off adding it as duplicate
I think I have found a solution but it involves iterating through all the custom XML parts, deleting the one you want to update and then add again:
IEnumerator e = workbook.CustomXMLParts.GetEnumerator();
CustomXMLPart p;
while (e.MoveNext())
{
p = (CustomXMLPart) e.Current;
//p.BuiltIn will be true for internal buildin excel parts
if (p != null && !p.BuiltIn && p.NamespaceURI == NS.NamespaceName)
p.Delete();
}

How to get an independent copy of an XDocument?

I'm trying to create a new XDocument as follows:
var xmlString = _documentDictionary[documentKey].ToString(SaveOptions.DisableFormatting);
XDocument xml = XDocument.Parse(xmlString);
I now have xml which I would have though was a stand-alone instance of a document because I extracted the string from the original document and created a new one from that.
But when I modify xml and then inspect the _documentDictionary[documentKey] I can see that the original document has been modified also.
How can I get a new independent document from the existing collection that I have?
Note:
I've tried these but it doesn't work:
var xmlString = _documentDictionary[documentKey].ToString(SaveOptions.DisableFormatting);
var copyDoc = new XDocument(xmlString);
and
var copyDoc = new XDocument(_documentDictionary[documentKey]);
There is a copy constructor defined for XDocument class:
var newDoc = new XDocument(xml);
You use this constructor to make a deep copy of an XDocument.
This constructor traverses all nodes and attributes in the document
specified in the other parameter, and creates copies of all nodes as
it assembles the newly initialized XDocument.
Quick test
var doc = new XDocument(new XElement("Test"));
var doc2 = new XDocument(doc);
doc.Root.Name = "Test2";
string name = doc.Root.Name.ToString();
string name2 = doc2.Root.Name.ToString();
name is "Test2" and name2 is "Test", what proofs that changes made on doc don't affect doc2.
Try to copy constructor, like;
var newDoc = new XDocument(xml);
From MSDN:
You use this constructor to make a deep copy of an XDocument.
This constructor traverses all nodes and attributes in the document
specified in the other parameter, and creates copies of all nodes as
it assembles the newly initialized XDocument.

C# - Unable to save a customized linq-to-xml file, with new elements in it?

I'm creating a new XDocument and inserting a root element "profiles" in it, then saving.
if (!System.IO.File.Exists("profiles.xml"))
{
XDocument doc = new XDocument(
new XElement("profiles")
);
doc.Save("profiles.xml", SaveOptions.None);
}
And then later I wanna take users input and add profiles into the already created xml file:
XElement profile =
new XElement(Player.Name,
new XElement("level", Player.Level),
new XElement("cash", Player.Cash)
);
XDocument doc = XDocument.Load("profiles.xml");
List<XElement> profiles = doc.Root.Elements().ToList();
for (int i = 0; i < profiles.Count; i++)
{
if (profiles[i].Name.ToString() == Player.name)
{
profiles[i] = profile;
return;
}
}
profile.Add(profile);
doc.Save("profiles.xml", SaveOptions.None);
But for some reason, it will never add any new profiles?
EDIT: Also, if I manually create a new profile into the xml file, it won't customize either, so the problem is within Saving the file?
You're never actually doing anything to change any of the elements within the XDocument that doc refers to:
If you find an element with the existing name, you're modifying the list, but that won't modify the document. You probably want to use XElement.ReplaceWith:
profiles[i].ReplaceWith(profile);
Note that in this case you're not even trying to save the XML file again (due to the return statement), so it's not really clear what you're trying to achieve in this case.
If you don't find the element, you're adding the profile element to itself, which certainly isn't going to modify the document. I suspect you want:
doc.Root.Add(profile);
In other words, add the new profile element as a new final child of the root element.
EDIT: Here's a different approach to try instead - I'm assuming any one name should only occur once:
XDocument doc = XDocument.Load("profiles.xml");
var existingElement = doc.Root
.Elements()
.Where(x => x.Name.ToString() == Player.name)
.FirstOrDefault();
if (existingElement != null)
{
existingElement.ReplaceWith(profile);
}
else
{
doc.Root.Add(profile);
}
doc.Save("profiles.xml", SaveOptions.None);
Also, I would strongly advise you not to use the player name as the element name. Use it as an attribute value or text value instead, e.g.
XElement profile =
new XElement("player",
new XAttribute("name", Player.Name),
new Attribute("level", Player.Level),
new XAttribute("cash", Player.Cash)
);
That way you won't have problems if the player name has spaces etc. You'd then need to change your query to:
var existingElement = doc.Root
.Elements()
.Where(x => (string) x.Attribute("name)" == Player.name)
.FirstOrDefault();

how to create N Level xml file

I need to create xml file that have N Level.
for ex. in my below example I have 'AlbumDetails' is root element and 'PrintPackage' is another child root and 'UpgradePackage' is another child root.
Can any one let me know how can i make N Level/Multi Level XML in c#.
<AlbumDetails>
<Album Id="203">
<Institute>Oxford</Institute>
<Venue>Wallingford School</Venue>
<PrintPackage>
<SizeName>Combination Pack</SizeName>
<Price>1.00</Price>
<Weight>60.00</Weight>
<UpgradePackage>
<SizeName>Upgrade 1</SizeName>
<Price>1.00</Price>
<Weight>60.00</Weight>
</UpgradePackage>
<SizeName>Standard Pack</SizeName>
<Price>90.0000</Price>
<Weight>600.0000</Weight>
</PrintPackage>
</Album>
</AlbumDetails>
You are looking for the XmlWriter class.
Update: In case you want to create a document similar to the one above:
var builder = new StringBuilder();
using (var writer = XmlWriter.Create(builder))
{
writer.WriteStartElement("AlbumDetails");
writer.WriteStartElement("Album");
writer.WriteAttributeString("Id", "203");
writer.WriteElementString("Venue", "Wallingford School");
writer.WriteStartElement("PrintPackage");
.... etc.
writer.WriteEndElement(); // close PrintPackage
writer.WriteEndElement(); // close Album
writer.WriteEndElement(); // close AlbumDetails
}
Console.WriteLine(builder.ToString());
Use XDocument and XElement from System.Xml.Linq ( Linq2Xml )
XDocument doc = new XDocument(new XDeclaration("1.0","utf-8","true"),
new XElement("AlbumDetails",
new XElement("Album",new XAttribute("Id","203"),
new XElement("Institute","Oxford"),
new XElement("Venue","Wallingford School")
...
)
)
);
If you are just looking for XElement only, you can build it up in a similar way. You can have a processingElement and create the XElement based on your logic and do
doc.Add(processingElement);
or
ele.Add(processingElement);
They're not really "child roots" - they're just elements which have other child elements.
Personally I'd use LINQ to XML. It's by far the simplest XML API I've used. For example:
var element = new XElement("AlbumDetails",
new XElement("Album",
new XAttribute("ID", 203"),
new XElement("Institute", "Oxford"),
new XElement("Venue", "Wallingford School"),
new XElement("PrintPackage",
new XElement("SizeName", "Combination Pack"),
// etc
new XElement("UpgradePackage",
new XElement("SizeName", "Upgrade 1"),
// etc
)
)
);
Of course, you don't have to build up everything in a single statement - you can add child nodes separately, potentially constructing them entirely separately. Indeed, you may want a separate method to create each "container" element.

Categories

Resources