How to dynamically populate XElement (linq to xml) - c#

Is there a way I can dynamically add a new XElement to form child nodes as in the example below?
XElement xEl = new XElement(
new XElement("Root",
// ** Is there a way I can do this:
// for(MyObject mObj in myObjects) {
// if (IsXmlObj(mObj)){
// new XElement(mObj.Name, mObj.Value);
// }
// }
);
I would like to iterate through an object list to form the sub nodes.
What if I now modify the iterating part to become:
// for(MyObject mObj in myObjects) {
// if (IsXmlObj(mObj)){
// if (mObject.Name=="Small"){ mObject.Name="Big";}
// new XElement(mObj.Name, mObj.Value);
// }
// }

Use a Select this way:
var xEl = new XElement("Root",myObjects.Where(mObj=>IsXmlObj(mObj))
.Select(mObj=> new XElement(mObj.Name, mObj.Value)));

Related

Edit existing XML file tree

I have problem with an editting existing xml file. I was looking for a solution but I can not find solution which I need. Can someone help me please?
Here is my code:
private void referenceToXML(string path)
{
var filePath = path;
var xmlDoc = XDocument.Load(filePath);
var parentElement = new XElement("Items");
var firstNameElement = new XElement("Item");
firstNameElement.SetAttributeValue("name", question.text);
var lastNameElement = new XElement("Cathegory", SM.text);
parentElement.Add(firstNameElement);
firstNameElement.Add(lastNameElement);
var rootElement = xmlDoc.Element("ItemCollection");
rootElement.Add(parentElement);
xmlDoc.Save(path);
}
And here is result of the code:
https://pastebin.com/LKGJER38
But I need this:
https://pastebin.com/RRC75pR8
I will appreciate every help.
Add to the Items Element instead :
private void referenceToXML(string path)
{
var xmlDoc = XDocument.Load(path);
xmlDoc.Element("ItemCollection").Element("Items").Add(
new XElement("Item", new XAttribute("name", question.text), SM.text));
xmlDoc.Save(path);
}
Your problem is that you are unconditionally adding an <Items> element every time you add an <Item> element. Instead, you need to check whether such an element exists, and if so, use it. The following extension method makes that easy:
public static partial class XNodeExtensions
{
public static XElement GetOrAddElement(this XContainer container, XName name)
{
if (container == null || name == null)
throw new ArgumentNullException();
var element = container.Element(name);
if (element == null)
container.Add(element = new XElement(name));
return element;
}
}
Now you can modify your referenceToXML method to use it as follows:
private static void referenceToXML(string path, string questionText, string smText)
{
AddItem(XDocument.Load(path), questionText, smText).Save(path);
}
static XDocument AddItem(XDocument doc, string questionText, string smText)
{
var firstNameElement = new XElement("Item");
firstNameElement.SetAttributeValue("name", questionText);
var lastNameElement = new XElement("Cathegory", smText);
firstNameElement.Add(lastNameElement);
// Get or create the root element ItemCollection
var root = doc.GetOrAddElement("ItemCollection");
// Get or create the Items list
var items = root.GetOrAddElement("Items");
// Add the item
items.Add(firstNameElement);
return doc;
}
Demo fiddle here.
Note I modified your code to make question.text and SM.text arguments instead of class members for clarity and testing purposes.

How can I add XElement iterating elements?

This is my code:
var xml = new XElement("test", new[] {
new XElement("group", new[] {
new XElement("date", dateNow.ToString("dd/MM/yyyy HH:mm:ss"))
}),
new XElement("users", new[] {
foreach(var item in in PlaceHolderCustom.Controls)
{
new XElement("nome", ((TextBox)item.FindControl("myTextBox")).Text)
}
})
});
I'd like to set in the xml some fixed fields (within the element "group") and some that would iterate across a placeholder. But the syntax seems to be wrong when I try to add a new "iterating" list.
Where am I wrong?
Use linq .Select to perform the foreach. Also when you create the array the new [] {} syntax is valid only for new string[]. In your case use:
new XElement[] {}
Or because the method gets a params object[] you can just give each new XElement independently without wrapping with an array
So showing both ways of passing the collection of XElements:
var xml = new XElement("test",
new XElement("group", new XElement[] {
new XElement("date", dateNow.ToString("dd/MM/yyyy HH:mm:ss"))
}),
new XElement("users", PlaceHolderCustom.Control.Select(item =>
new XElement("nome", ((TextBox)item.FindControl("myTextBox")).Text)).ToArray())
);

How to check if an XML node exists in the children of another node?

I want to add an XmlNode to another XmlNode if it doesn't contain this node (the comparison should be based on the node name and its contents)
System.Xml.XmlDocument doc;
...
XmlNode newNode = doc.CreateElement(name);
newNode.InnerXml = something
XmlNode parentNode = doc.GetElementsByTagName(parentName);
if (parentNode.???? (newNode))
{
parentNode.AppendChild(newNode);
}
How can I check this existence? parentNode.ChildNodes doesn't have a Contain method.
I think this will do the trick:
private void doSomething()
{
XmlDocument doc = new XmlDocument();
XmlNode newNode = doc.CreateElement("name");
newNode.InnerXml = "something";
XmlNode parentNode = doc.GetElementsByTagName("parentName")[0];
// I just stuck an index on end of above line...
// Note that GetElementsByTagName returns an XmlNodeList
int huh = 0;
foreach (XmlNode n in parentNode.ChildNodes)
{
// If I understood you correctly, you want these checks?
if (n.InnerXml == newNode.InnerXml && n.Name == newNode.Name) huh++;
}
if (huh == 0) parentNode.AppendChild(newNode);
}
You could do this using LINQ to XML making use of the XNode.DeepEquals method to compare your child nodes for equality. An example might look like this - the duplicateChild will not be added but newChild will be:
var doc = new XDocument(
new XElement("parent",
new XElement("child", 1)));
var parent = doc.Descendants("parent").Single();
var duplicateChild = new XElement("child", 1);
var newChild = new XElement("child", 2);
if (!parent.Elements().Any(e => XNode.DeepEquals(e, duplicateChild)))
{
parent.Add(duplicateChild);
}
if (!parent.Elements().Any(e => XNode.DeepEquals(e, newChild)))
{
parent.Add(newChild);
}
A demo here: https://dotnetfiddle.net/1t4Q1b

Serializing a list of objects to XDocument

I'm trying to use the following code to serialize a list of objects into XDocument, but I'm getting an error stating that "Non white space characters cannot be added to content
"
public XDocument GetEngagement(MyApplication application)
{
ProxyClient client = new ProxyClient();
List<Engagement> engs;
List<Engagement> allEngs = new List<Engagement>();
foreach (Applicant app in application.Applicants)
{
engs = new List<Engagement>();
engs = client.GetEngagements("myConnString", app.SSN.ToString());
allEngs.AddRange(engs);
}
DataContractSerializer ser = new DataContractSerializer(allEngs.GetType());
StringBuilder sb = new StringBuilder();
System.Xml.XmlWriterSettings xws = new System.Xml.XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
using (System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(sb, xws))
{
ser.WriteObject(xw, allEngs);
}
return new XDocument(sb.ToString());
}
What am I doing wrong? Is it the XDocument constructor that doesn't take a list of objects? how do solve this?
I would think that last line should be
return XDocument.Parse(sb.ToString());
And it might be an idea to cut out the serializer altogether, it should be easy to directly create an XDoc from the List<> . That gives you full control over the outcome.
Roughly:
var xDoc = new XDocument( new XElement("Engagements",
from eng in allEngs
select new XElement ("Engagement",
new XAttribute("Name", eng.Name),
new XElement("When", eng.When) )
));
The ctor of XDocument expects other objects like XElement and XAttribute. Have a look at the documentation. What you are looking for is XDocument.Parse(...).
The following should work too (not tested):
XDocument doc = new XDocument();
XmlWriter writer = doc.CreateNavigator().AppendChild();
Now you can write directly into the document without using a StringBuilder. Should be much faster.
I have done the job this way.
private void button2_Click(object sender, EventArgs e)
{
List<BrokerInfo> listOfBroker = new List<BrokerInfo>()
{
new BrokerInfo { Section = "TestSec1", Lineitem ="TestLi1" },
new BrokerInfo { Section = "TestSec2", Lineitem = "TestLi2" },
new BrokerInfo { Section = "TestSec3", Lineitem ="TestLi3" }
};
var xDoc = new XDocument(new XElement("Engagements",
new XElement("BrokerData",
from broker in listOfBroker
select new XElement("BrokerInfo",
new XElement("Section", broker.Section),
new XElement("When", broker.Lineitem))
)));
xDoc.Save("D:\\BrokerInfo.xml");
}
public class BrokerInfo
{
public string Section { get; set; }
public string Lineitem { get; set; }
}

How to add another top level node

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)

Categories

Resources