Looping through nodes with XPathNavigator - c#

Hi below is a sample of the xml i am using. I been through allsorts of options I can think of to be able to start at the personData node and iterate the results, and nothing i seem to try works unless I navigate manually through each child node from the root down. Can anyone advise on how I can do this without starting at the root
My code currently is
using (var r = File.OpenText(#"C:\S\sp.xml"))
{
XPathDocument document = new XPathDocument(XmlReader.Create(r));
XPathNavigator xPathNav = document.CreateNavigator();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xPathNav.NameTable);
nsmgr.AddNamespace("g2", "http://person.transferobject.com/xsd");
XPathNodeIterator xni = xPathNav.Select("/g2:companys/g2:company/g2:person/g2:personData", nsmgr);
foreach (XPathNavigator nav in xni)
Console.WriteLine(nav.Name);
}
Xml
<?xml version="1.0" encoding="UTF-8"?>
<Header xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<dataSource xmlns="http://person.transferobject.com/xsd">IG2</dataSource>
<dateTime xmlns="http://person.transferobject.com/xsd">Thu Mar 21 15:56:42 GMT 2013</dateTime>
<formatVersion xmlns="http://person.transferobject.com/xsd">2.0</formatVersion>
<companys xmlns="http://person.transferobject.com/xsd">
<company>
<errorMessages xsi:nil="true"/>
<person>
<personData>
<address>
<address1 xmlns="http://transferobject.com/xsd">37 Smith St</address1>
<county xmlns="http://transferobject.com/xsd">COUNTY-37</county>
<postcode xmlns="http://transferobject.com/xsd">Po12 123</postcode>
</address>
<basicDetails>
<currentFirstName xmlns="http://transferobject.com/xsd">Fred</currentFirstName>
<currentLastName xmlns="http://transferobject.com/xsd">Bloggs</currentLastName >
<currentStage xmlns="http://transferobject.com/xsd">H1</currentStage>
<currentGroup xmlns="http://transferobject.com/xsd">3</currentGroup>
<dob xmlns="http://transferobject.com/xsd">2000-04-25</dob>
<email xmlns="http://transferobject.com/xsd">AN#AN.AOM</email>
<entryDate xmlns="http://transferobject.com/xsd">2003-09-03</entryDate>
</basicDetails>
</personData>
<personData>
<address>
<address1 xmlns="http://transferobject.com/xsd">37 Smith St</address1>
<county xmlns="http://transferobject.com/xsd">COUNTY-37</county>
<postcode xmlns="http://transferobject.com/xsd">Po12 123</postcode>
</address>
<basicDetails>
<currentFirstName xmlns="http://transferobject.com/xsd">John</currentFirstName>
<currentLastName xmlns="http://transferobject.com/xsd">Bloggs</currentLastName >
<currentStage xmlns="http://transferobject.com/xsd">H1</currentStage>
<currentGroup xmlns="http://transferobject.com/xsd">3</currentGroup>
<dob xmlns="http://transferobject.com/xsd">1999-04-25</dob>
<email xmlns="http://transferobject.com/xsd">AN#AN.AOM</email>
<entryDate xmlns="http://transferobject.com/xsd">2003-09-03</entryDate>
</basicDetails>
</personData>
</person>
</company>
</companys>
</header>

I know you're using XPath, but as you have an answer with XPath I'll give one using Linq
using System;
using System.Linq;
using System.Xml.Linq;
namespace xmlTest
{
class Program
{
static void Main()
{
XDocument doc = XDocument.Load("C:\\Users\\me\\Desktop\\so.xml");
var personDataDetails = (from p in doc.Descendants().Elements()
where p.Name.LocalName == "personData"
select p);
foreach (var item in personDataDetails)
{
Console.WriteLine(item.ToString());
}
Console.ReadKey();
}
}
}

Are you just asking how you can iterate through the personData nodes without listing the full path? If that's what you want to do, you can just do this:
XPathNodeIterator xni = xPathNav.Select("//g2:personData", nsmgr);

Related

How to move up the XML `Element` one step higher and change its `XName` to this format `parent_child` using `Linq to XML` in C#

I am trying to modify the following XML doc (MRE) using Linq to XML:
Move the school name and address one step higher or at the level of its parent—school element.
At the same time, change its name to <School_Name> and <School_Address>.
ORIGINAL
<?xml version="1.0" encoding="utf-8"?>
<GreatGrandParent>
<GrandParent>
<Parent>
<Child>
<Name>John</Name>
<Address>California</Address>
<Sex>Male</Sex>
<Age>18</Age>
<School>
<Name>Cool School</Name>
<Address>California</Address>
</School>
</Child>
<Child>
<Name>Mary</Name>
<Address>New Orleans</Address>
<Sex>Female</Sex>
<Age>16</Age>
<School>
<Name>Pretty School</Name>
<Address>New Orleans</Address>
</School>
</Child>
<Parent>
<GrandParent>
<GreatGrandParent>
TARGET MODIFICATION
<?xml version="1.0" encoding="utf-8"?>
<GreatGrandParent>
<GrandParent>
<Parent>
<Child>
<Name>John</Name>
<Address>California</Address>
<Sex>Male/Sex>
<Age>18</Age>
<School_Name>Cool School</School_Name>
<School_Address>California</School_Address>
</Child>
<Child>
<Name>Mary</Name>
<Address>New Orleans</Address>
<Sex>Female/Sex>
<Age>16</Age>
<School_Name>Pretty School</School_Name>
<School_Address>New Orleans</School_Address>
</Child>
<Parent>
<GrandParent>
<GreatGrandParent>
HERE'S WHAT I HAVE DONE SO FAR:
XDocument doc = XDocument.Load(#"D:\Xml\Childs Profile.xml");
XElement root = doc.XPathSelectElement("//Parent");
IEnumerable<XElement> rootlists = root.Descendants("Child").ToList();
foreach (var rootlist in rootlists)
{
XElement lists = root.Element("Child");
XElement name = root.Element("Child").Element("School").Element("Name");
XElement address = root.Element("Child").Element("School").Element("Address");
list.AddFirst(name);
list.AddFirst(address);
XElement school = root.Element("Child").Element("School");
school.Remove();
}
doc.Save(#"D:\Xml\Childs Profile.xml");
ISSUES: The foreach above does not move to next child.
PENDING: Change the XName to <School_Name> and <School_Address> either before or after moving.
You can try this:
XDocument doc = XDocument.Load(#"the.xml");
var child = doc.Descendants("Child");
var school = child.Elements("School");
string name = $"{school.First().Name}_{school.Elements("Name").First().Name}";
string address = $"{school.First().Name}_{school.Elements("Address").First().Name}";
foreach (var s in school)
{
s.Parent.Add(new XElement(name, s.Element("Name").Value));
s.Parent.Add(new XElement(address, s.Element("Address").Value));
}
school.Remove();
doc.Save(#"out.xml");

Unable to parse xml string using Xdocument and Linq

I would like to parse the below xml using XDocument in Linq.
<?xml version="1.0" encoding="UTF-8"?>
<string xmlns="http://tempuri.org/">
<Sources>
<Item>
<Id>1</Id>
<Name>John</Name>
</Item>
<Item>
<Id>2</Id>
<Name>Max</Name>
</Item>
<Item>
<Id>3</Id>
<Name>Ricky</Name>
</Item>
</Sources>
</string>
My parsing code is :
var xDoc = XDocument.Parse(xmlString);
var xElements = xDoc.Element("Sources")?.Elements("Item");
if (xElements != null)
foreach (var source in xElements)
{
Console.Write(source);
}
xElements is always null. I tried using namespace as well, it did not work. How can I resolve this issue?
Try below code:
string stringXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><string xmlns=\"http://tempuri.org/\"><Sources><Item><Id>1</Id><Name>John</Name></Item><Item><Id>2</Id><Name>Max</Name></Item><Item><Id>3</Id><Name>Ricky</Name></Item></Sources></string>";
XDocument xDoc = XDocument.Parse(stringXml);
var items = xDoc.Descendants("{http://tempuri.org/}Sources")?.Descendants("{http://tempuri.org/}Item").ToList();
I tested it and it correctly shows that items has 3 lements :) Maybe you used namespaces differently (it's enough to inspect xDoc objct in object browser and see its namespace).
You need to concatenate the namespace and can directly use Descendants method to fetch all Item nodes like:
XNamespace ns ="http://tempuri.org/";
var xDoc = XDocument.Parse(xmlString);
var xElements = xDoc.Descendants(ns + "Item");
foreach (var source in xElements)
{
Console.Write(source);
}
This prints on Console:
<Item xmlns="http://tempuri.org/">
<Id>1</Id>
<Name>John</Name>
</Item><Item xmlns="http://tempuri.org/">
<Id>2</Id>
<Name>Max</Name>
</Item><Item xmlns="http://tempuri.org/">
<Id>3</Id>
<Name>Ricky</Name>
</Item>
See the working DEMO Fiddle

Adding a parent element to existing elements using XDocument, XMLDocument or XPath?

For the following problem, which API requires less code, XMLDocument or XDoc? Is there any other approach besides XMLDocument, XDoc or XPath that is more suitable for this task? (Ignoring performance and file sizes are small)
Trying to turn some elements into a child of a node that that appear in the list e.g. (the indentation is not important, please ignore it)
<PersonList>
<Person>
<Name>Name1</Name>
<LName>LName1</LName>
<Phone>Phone</Phone>
</Person>
<Person>
<Name>Name2</Name>
<LName>LName2</LName>
<Phone>Phone2</Phone>
</Person>
<Person>
<Name>Name3</Name>
<LName>LName3</LName>
<Phone>Phone3</Phone>
</Person>
</PersonList>
I am trying to turn them into this
<PersonList>
<Person>
<PersonDetailsList>
<Name>Name1</Name>
<LName>LName1</LName>
<Phone>Phone</Phone>
</PersonDetailsList>
</Person>
<Person>
<PersonDetailsList>
<Name>Name2</Name>
<LName>LName2</LName>
<Phone>Phone2</Phone>
</PersonDetailsList>
</Person>
<Person>
<PersonDetailsList>
<Name>Name3</Name>
<LName>LName3</LName>
<Phone>Phone3</Phone>
</PersonDetailsList>
</Person>
</PersonList>
Xml Linq works very well :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication7
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> people = doc.Descendants("Person").ToList();
foreach (XElement person in people)
{
var children = person.Nodes().ToList();
person.ReplaceWith(new XElement("Person", new object[] {
new XElement("PersonDetailsList", children)
}));
}
}
}
}
Just to add parent node
var doc = XDocument.Parse(xml);
var elems = doc.Descendants("Person");
foreach (XElement elem in elems)
{
elem.ReplaceWith(new XElement("PersonDetailsList", elem));
}

How to add Namespace and Declaration to the existing XML

how to add Namespace and Declaration to the existing xml.
My XML
<Order>
<child1></child1>
</Order>
Expected
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Order xmlns="http://a.com/a">
<child1></child>
</Order>
Assuming you're only changing the element namespaces and ignoring any attributes, you can parse this using LINQ to XML and then change every element's qualified name:
var doc = XDocument.Parse(xml);
XNamespace ns = "http://a.com/a";
foreach (var element in doc.Descendants())
{
element.Name = ns + element.Name.LocalName;
}

XPATH returns null for XML with namespace

I have the following XML.
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://localhost/gsainis/GsaInisWebService">
<string>
<gsafeed>
<group action="add">
<record>
......
......
</record>
</group>
</gsafeed>
</string>
</ArrayOfString>
I am using C# code (.NET 4.0) to parse this XML. I am using the code below to select all <record> nodes in the above XML.
XmlNamespaceManager xmlnsmgr = new XmlNamespaceManager(INISRecordXMLdoc.NameTable);
xmlnsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xmlnsmgr.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
xmlnsmgr.AddNamespace(string.Empty, "http://localhost/gsainis/GsaInisWebService");
foreach (XmlNode node in INISRecordXMLdoc.SelectNodes("//ArrayOfString/string/gsafeed/group/record",xmlnsmgr))
{
//Do something
}
The problem is the foreach loop is never executed. What should be correct XPath to be used such that I get all the <record> nodes?
Try this - I've had trouble in the past with having an "empty" XML prefix:
XmlNamespaceManager xmlnsmgr = new XmlNamespaceManager(INISRecordXMLdoc.NameTable);
xmlnsmgr.AddNamespace("ns", "http://localhost/gsainis/GsaInisWebService");
foreach (XmlNode node in INISRecordXMLdoc.SelectNodes("//ns:ArrayOfString/ns:string/ns:gsafeed/ns:group/ns:record", xmlnsmgr))
{
// Do something
}
Use something other than an empty string - and use that XML namespace prefix in your XPath.

Categories

Resources