Please consider this XML:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</Employees>
That is content of a XElement variable.Now I have another XElement variable with this content:
<Person>
<ID>1001</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
I want to add this XEelemnt variable to first XElement in a specific position (for example as second Person tag). How I can do this?
thanks
First you need to load the xml string, second you get the position where you want to insert the xml, then insert the new xml. Here is an example how to do it.
var reader = new StringReader(#"<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</Employees>");
var xdoc = XDocument.Load(reader);
xdoc.Element("Employees").
Elements("Person").
First().
AddAfterSelf(new XElement("Person",
new XElement("ID", 1001),
new XElement("Name", "Aba"),
new XElement("LName", "Aba")));
var sb = new StringBuilder();
var writer = new StringWriter(sb);
xdoc.Save(writer);
Console.WriteLine(sb);
UPDATE
If you want to insert by index, just get the element first. For example you want to insert as second position, then you need to get the first index (index = 0).
var xdoc = XDocument.Load(reader);
xdoc.Element("Employees").
Elements("Person").
ElementAt(0).
AddAfterSelf(new XElement("Person",
new XElement("ID", 1001),
new XElement("Name", "Aba"),
new XElement("LName", "Aba")));
PS: For simplicity purpose I didn't add nullity check.
Related
If I have the following xml:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</Employees>
I want to add a new node, after an indexed node, and add the remaining person nodes as the children of this new node.
So the new xml after adding would look like this:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<RefNode>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</RefNode>
</Employees>
So far I have tried using
ElementAt(index).AddAfterSelf()
but that just adds it next in line, and doesn't add the next two Persons as children.
You could do as following. Have added comments in code for better understanding
var xdoc = XDocument.Parse(xml);
// Unclear how you are identifying the node after which the change has to happen. For sake of example, using ID
var selectedNode = xdoc.Descendants("Person")
.First(x=>Convert.ToInt32(x.Element("ID").Value)==1000);
// Collection of nodes that would be added as child of newly inserted node
var nodeAfterSelectedNode = xdoc.Descendants("Person")
.SkipWhile(x=>x==selectedNode)
.ToList();
// Create the new node with previously identified 'nodeAfterSelectedNode' as Children
var newElement = new XElement("RefNode",nodeAfterSelectedNode);
// Remove the existing Nodes (ones that are being moved to child)
foreach(var node in nodeAfterSelectedNode)
{
node.Remove();
}
// add the new node
selectedNode.AddAfterSelf(newElement);
var newXml = xdoc.ToString();
Output
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<RefNode>
<Person>
<ID>1002</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
</RefNode>
</Employees>
Output Sample
I'm trying to build up Xml that looks like the following (taken from another question) but using the XElement/XNamespace classes:
<person xmlns:json='http://james.newtonking.com/projects/json' id='1'>
<name>Alan</name>
<url>http://www.google.com</url>
<role json:Array='true'>Admin</role>
</person>
This is so I can serialize using Newtonsoft.Json.JsonConvert.SerializeXmlNode() and maintain correct arrays.
The problem I'm having is creating json:Array='true'.
Other examples show XmlDocument classes or raw creation of Xml string, but is there a way to achieve it using XElement? I've tried several things with XNamespace to attempt to create the "json" prefix without success.
Yes, you can achieve it with XElement. For example:
XNamespace json = "http://james.newtonking.com/projects/json";
XDocument xml = new XDocument(new XElement("person",
new XAttribute(XNamespace.Xmlns + "json", json),
new XAttribute("id", 1),
new XElement("name", "Alan"),
new XElement("url", "http://www.google.com"),
new XElement("role", new XAttribute(json + "Array", true), "Admin")));
Will produce the following:
<person xmlns:json="http://james.newtonking.com/projects/json" id="1">
<name>Alan</name>
<url>http://www.google.com</url>
<role json:Array="true">Admin</role>
</person>
Very Simple. I have 2 documents
Doc1
<Person>
<Name>Bob</Name>
</Person>
Doc2
<Animal>
<Name>Zippy</Name>
</Animal>
And I want to create
Doc3
<Person>
<Name>Bob</Name>
</Person>
<Animal>
<Name>Zippy</Name>
</Animal>
The code I have below is close but insert the XML INSIDE the other one and I don't want that
string xmlUserData = GetUserData(fileId);
string xmlPurchaseDate = GetPurchaseData();
XDocument xdocUserData = XDocument.Parse(xmlUserData);
XDocument xdocPurchaseDate = XDocument.Parse(xmlPurchaseDate);
XElement xe1 = xdocUserData.Descendants("USERDATA").FirstOrDefault();
XElement xe2 = xdocPurchaseDate.Descendants("PurchaseAdvice").FirstOrDefault();
xe1.Add(xe2.Nodes());
Yes, you can wrap elements in a root:
XDocument doc = new XDocument();
XElement rootElement = new XElement("Root");
rootElement.Add(new XElement("person"));
rootElement.Add(new XElement("animal"));
doc.Add(rootElement);
gives:
<Root>
<person />
<animal />
</Root>
Using the following example xml containing one duplicate:
<Persons>
<Person>
<PersonID>7506</PersonID>
<Forename>K</Forename>
<Surname>Seddon</Surname>
<ChosenName />
<MiddleName />
<LegalSurname />
<Gender>Male</Gender>
</Person>
<Person>
<PersonID>6914</PersonID>
<Forename>Clark</Forename>
<Surname>Kent</Surname>
<ChosenName>Clark</ChosenName>
<MiddleName />
<LegalSurname>Kent</LegalSurname>
<Gender>Male</Gender>
</Person>
<Person>
<PersonID>6914</PersonID>
<Forename>Clark</Forename>
<Surname>Kent</Surname>
<ChosenName>Clark</ChosenName>
<MiddleName />
<LegalSurname>Kent</LegalSurname>
<Gender>Male</Gender>
</Person>
</Persons>
I have the following code where I am trying to build an XDocument with the output of an XPath query that filters the duplicate elements:
string outputXml = null;
var original = XDocument.Parse(xmlString);
string xpathFilterDups = "//Person[not(PersonID = following::Person/PersonID)]";
XDocument people = new XDocument("Persons", original.XPathSelectElements(xpathFilterDups));
outputXml = people.ToString();
I get the error:
An exception of type 'System.ArgumentException' occurred in System.Xml.Linq.dll but was not handled in user code
Non white space characters cannot be added to content.
On this line:
XDocument people = new XDocument("Persons", original.XPathSelectElements(xpath));
What am I doing wrong? :-\
You can ignore pretty much all your code, the issue is just this:
XDocument people = new XDocument("Persons");
You can't create an XDocument containing a string, you need to add an element:
XDocument people = new XDocument(
new XElement("Persons",
original.XPathSelectElements(xpathFilterDups)));
I need some help with C# and XML.
I'm building a web application and need to present information about number of peope with gender Male in a specific department (How many men in Dep1).
This is my XML file:
<company>
<department>
<departmentname>Dep 1</departmentname>
<people>
<person>
<name>Sean</name>
<date>2013-10-10</date>
<gender>male</gender>
<age>40</age>
</person>
<person>
<name>John</name>
<date>2013-10-18</date>
<gender>male</gender>
<age>45</age>
</person>
<person>
<name>Linda</name>
<date>2013-09-10</date>
<gender>female</gender>
<age>42</age>
</person>
<person>
<name>Bob</name>
<date>2013-10-01</date>
<gender>male</gender>
<age>35</age>
</person>
</people>
</department>
<department>
<departmentname>Dep 2</departmentname>
<people>
<person>
<name>Art</name>
<date>2013-09-10</date>
<gender>male</gender>
<age>38</age>
</person>
<person>
<name>Christina</name>
<date>2013-10-20</date>
<gender>female</gender>
<age>45</age>
</person>
<person>
<name>Marie</name>
<date>2013-09-10</date>
<gender>female</gender>
<age>49</age>
</person>
</people>
</department>
</company>
My code (not finished and not working):
XElement company= XElement.Load(Server.MapPath("myXML.xml"));
XElement department= (from p in company.Elements("department")
where p.Element("departmentname").Value == 'Dep 1' && p.Element
("gender").Value == 'male'
select p).Count;
numberTextBox.Text = department.Element; //???How to write to textbox
var department = (from p in company.Elements("department")
where p.Element("departmentname").Value == "Dep 1" && p.Element
("gender").Value == "male"
select p).Count();
You're not retrieving an XElement, you're retrieving an int (which is what Count returns).
Using var will infer this for you.
And per my comment: kon -> gender.
Let's separate out some of the elements of the search to make it easier to follow:
XElement depName = company.Descendants("departmentname")
.Where(x => x.Value == "Dep 1")
.FirstOrDefault();
XElement[] men = depName.Parent.Descendants("gender")
.Where(x => x.Value == "male")
.Where(x => (int)x.Parent.Element("age") > 40)
.ToArray();
int count = men.Length;
depName.Parent is the department element. From that we get all the gender's with the value male. So the variable men is only the genders. If you want the whole person element, use the men's Parent property to get that. For example:
foreach(XElement man in men)
Console.Writeline(man.Parent.Element("name").Value);
To assign to the textbox:
numberTextBox.Text = count.ToString(); // or men.Length.ToString()
Hope this will help you..
XmlDocument doc = new XmlDocument();
doc.Load(your document);
var items = doc.GetElementsByTagName("gender");
var x=items.Count;
int malecount=doc.SelectNodes("gender[. = \"male\"]").Count;
You could try just XMLDocument and Xpath, just use the following code
var xpath = "/company/department[departmentname='Dep 1']/people/person[/company/department/people/person/gender='male']/name";
var xmldoc = new XmlDocument();
xmldoc.Load("company.xml");
var result = xmldoc.SelectNodes(xpath);
if (result != null) Assert.AreEqual(4,result.Count);
Hope this helps.