XmlDocument.SelectNodes and XPath Navigation - c#

I have XML code that looks similar to this:
<BookStore xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Book>
<bookGenre>Fantasy</bookGenre>
<bookTitle>A Storm of Swords</bookTitle>
<authorInformation>
<authorId>12345</authorId>
<authorName>
<firstName>George</firstName>
<middleInitial>R.R.</middleInitial>
<lastName>Martin</lastName>
</authorName>
</authorInformation>
</Book>
<customer>
<customerData />
</customer>
</BookStore>
The <customer>node may or may not have child nodes, depending on user input.
I am trying to use XmlDocument.SelectNodes and XPath navigation to select <BookStore>, <customer>, and any nodes contained within <customer>.
I have been looking around and reading about XPath and .SelectNodes for a few hours but still don't seem to fully understand how they work. Would anyone have an explanation as to how to use them or how I could use them in my situation? If there are other ways to go about solving my problem I'm open to those too! (I am using C#)
EDIT: here's what I tried based on the stuff I read
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlStr);
XmlNode root = doc.DocumentElement;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
XmlNodeList nodeList = root.SelectNodes("descendant::customer:child::Node");
doc.Save(Console.Out);

Try xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication62
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement customer = doc.Descendants("customer").FirstOrDefault();
Boolean children = customer.HasElements;
}
}
}

Related

ArgumentException: The node to be inserted is from a different document context

I've searched Stackoverflow on this question, as well as other forums but no one seems to be making this the way I'm making.
By this I mean, in my code, instead of using XMLNode, I'm using XMLElement.
So, without further ado, my intention is to save in an already existing XML Document, a new Element is a child of other existing Elements besides the root.
This is an example on my XML File:
<ROOT>
<NOT_THIS_ONE>
</NOT_THIS_ONE>
<THIS_ONE>
</THIS_ONE>
</ROOT>
So, this is my code:
//XML File
TextAsset repository = Resources.Load("Repository") as TextAsset;
//Create XML Reference
XmlDocument xmlDocument = new XmlDocument();
//Load XML File into XML Reference
xmlDocument.LoadXml(repository.text);
//Root Node
XmlNode statsNode = GetRootNode();
//Get History Node
XmlNode thisOneNode = statsNode.ChildNodes.Item(1);
The GetRootNode() function is this:
//Create Xml Reference
XmlDocument xmlData = new XmlDocument();
//Load Xml File into Xml Reference
xmlData.LoadXml(repository.text);
//Get Root Node
return xmlData.ChildNodes.Item(1);
The thisOneNode gets the <THIS_ONE> Element as a Node (at least that's what I think it does).
Later on, I do this:
XmlElement childOfThisOne = xmlDocument.CreateElement("CHILD");
XmlElement pointsSession = xmlDocument.CreateElement("POINTS");
pointsSession.InnerText = points.ToString();
childOfThisOne.AppendChild(pointsSession);
thisOneNode.AppendChild(childOfThisOne);
xmlDocument.Save("Assets/Resources/GamePoints.xml");
And my intention with this would be something like:
<ROOT>
<NOT_THIS_ONE>
</NOT_THIS_ONE>
<THIS_ONE>
<CHILD>
<POINTS>102</POINTS>
</CHILD>
</THIS_ONE>
</ROOT>
But I get the error in the title: "ArgumentException: The node to be inserted is from a different document context."
And the line in question is this: thisOneNode.AppendChild(childOfThisOne);
Now, where I've searched and the articles I found, people were using XmlNode and even used an xmlDocument.ImportNode(); I tried that too and the same error occurred. Now, I don't how to fix this and I'm requesting your help on this one.
Thank you for your time and happy holidays!
Using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
#"<ROOT>
<NOT_THIS_ONE>
</NOT_THIS_ONE>
<THIS_ONE>
</THIS_ONE>
</ROOT>";
XDocument doc = XDocument.Parse(xml);
XElement thisOne = doc.Descendants("THIS_ONE").FirstOrDefault();
thisOne.Add(new XElement("CHILD", new XElement("POINTS", 102)));
doc.Save("Assets/Resources/GamePoints.xml");
}
}
}

Update a specific node in XML using XmlDocument

I am new to managing XML, I've read a couple of articles but I'm confused when it comes to a specific XML that I am working on. Can someone help me with the right statement? I just want to update the value of the ListStart, but I am getting an error when compiling. I am updating that part by this:
XmlDocument soapEnvelopeDocument = new XmlDocument();
soapEnvelopeDocument.Load(#"path");
XmlNode myNode = soapEnvelopeDocument.SelectSingleNode("descendant::cet:GetListCustomElement[cet:GetListCustom/cet:ListID='101']");
soapEnvelopeDocument.LastChild.InnerText = sDate;
<soapenv:Header/>
<soapenv:Body>
<cet:GetListCustomElement>
<!--Zero or more repetitions:-->
<cet:GetListCustom>
<cet:ListID>101</cet:ListID>
<cet:ListStart>13.11.2020</cet:ListStart>
</cet:GetListCustom>
</cet:GetListCustomElement>
</soapenv:Body>
</soapenv:Envelope>```
You only supplied a piece of the xml without the namespaces. With Xml linq you can get the element without the namespaces. See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement ListStart = doc.Descendants().Where(x => x.Name.LocalName == "ListStart").FirstOrDefault();
ListStart.SetValue("14.11.2020");
}
}
}

I need to read xml file data but due to attribute it does not work

i want to read xml file but due to Document node attribute it did not read file.
Code C#:
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(HttpContext.Server.MapPath("~/Content/Images/MMS-CREATE-ALLA-ALLAH2H1-23102018-000170-INP.xml"));
XmlNode settings = xmldoc.SelectSingleNode("Document[#xmlns='urn:iso:std:iso:20022:tech:xsd:pain.009.001.01']/MndtInitnReq/GrpHdr");
stu.BranchName = settings.SelectSingleNode("MsgId").InnerText;
XML FIle:
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.009.001.01">
<MndtInitnReq>
<GrpHdr>
<MsgId>10005226074</MsgId>
<CreDtTm>2018-10-23T15:20:56</CreDtTm>
</GrpHdr>
</MndtInitnReq>
</Document>
You have a namespace that must be used to get the data. Try Xml Linq :
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication75
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace ns = doc.Root.GetDefaultNamespace();
string msgId = (string)doc.Descendants(ns + "MsgId").FirstOrDefault();
XElement xCreDtTm = doc.Descendants(ns + "CreDtTm").FirstOrDefault();
//will give 1/1/01 when null
DateTime CreDtTm = xCreDtTm == null ? new DateTime() : (DateTime)xCreDtTm;
}
}
}
I don't think loading this xml should be a problem. I verified that by loading the xml you posted in an XmlDocument object.
However I think, your xpath to get "settings" node should have xml namespace in all tags after Document.
So the xpath should be "/[local-name()='Document' and namespace-uri()='urn:iso:std:iso:20022:tech:xsd:pain.009.001.01']/[local-name()='MndtInitnReq' and namespace-uri()='urn:iso:std:iso:20022:tech:xsd:pain.009.001.01']/*[local-name()='GrpHdr' and namespace-uri()='urn:iso:std:iso:20022:tech:xsd:pain.009.001.01']"

Object reference not set to an instance of an object when reading XML

Here is the XML file I need to process:
<?xml version="1.0" encoding="UTF-8"?>
<shipment-info xmlns="http://www.canadapost.ca/ws/shipment-v8">
<shipment-id>11111111</shipment-id>
<shipment-status>created</shipment-status>
<tracking-pin>123456789012</tracking-pin>
<links>
<link rel="self" href="https://xxx" media-type="application/vnd.cpc.shipment-v8+xml"/>
<link rel="details" href="https://xxx" media-type="application/vnd.cpc.shipment-v8+xml"/>
<link rel="group" href="https://xxx" media-type="application/vnd.cpc.shipment-v8+xml"/>
<link rel="price" href="https://xxx" media-type="application/vnd.cpc.shipment-v8+xml"/>
<link rel="label" href="https://xxx" media-type="application/pdf" index="0"/>
</links>
</shipment-info>
And I want to get the tracking pin value, here is the code for doing this:
// xml text
string xml = (xml source above)
// load xml
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
// getting tracking pin
XmlNode node = doc.SelectSingleNode("/shipment-info"); // problem start here -> node return null
Console.WriteLine(node["tracking-pin"].InnerText);
// getting self and label links
node = doc.SelectSingleNode("/shipment-info/links");
foreach (XmlNode child in node)
{
if (child.Attributes["rel"].Value == "self")
Console.WriteLine(child.Attributes["href"].Value);
else if (child.Attributes["rel"].Value == "label")
Console.WriteLine(child.Attributes["href"].Value);
}
Console.ReadLine();
However, for selecting "/shipment-info" it return a null value, and I don't know why this happen. How to handle it?
You have a namespace for which you need to account:
// load xml
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(doc.NameTable);
//Add the namespaces used in books.xml to the XmlNamespaceManager.
xmlnsManager.AddNamespace("veeeight", "http://www.canadapost.ca/ws/shipment-v8");
// getting tracking pin
XmlNode node = doc.SelectSingleNode("//veeeight:shipment-info", xmlnsManager); // problem start here -> node return null
see:
https://support.microsoft.com/en-us/kb/318545
You have a namespace issue. Try this XML Linq solution
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace ns = ((XElement)(doc.FirstNode)).Name.Namespace;
string tracking_pin = doc.Descendants(ns + "tracking-pin").FirstOrDefault().Value;
}
}
}
add that to your code
XmlNode root = doc.DocumentElement;

XmlDocument rearrange XmlNodes C#

I have an XML Document that stores all sorts of information about users of a system. Utlimately, the nodes that I am intersted in I hae outined below.
So, there is a user that has many user contents - I have included just books as an example.
<?xml version="1.0" encoding="UTF-8"?>
<user>
<userProperties>
<alias val="userAliasOne"/>
<id val="3423423"/>
</userProperties>
<userContent>
<userBooks>
<genre>
<book>
<title>Dummy Value</title>
</book>
</genre>
</userBooks>
</userContent>
</user>
I need to somehow restructure the XML, using XmlDocument and XmlNode so that it matches the below. (userBooks to become root node but all contents of userBooks - /genre/book/title - to stay inside userContent).
<?xml version="1.0" encoding="UTF-8"?>
<userBooks>
<user>
<userProperties>
<alias val="userAliasOne"/>
<id val="3423423"/>
</userProperties>
<userContent>
<genre>
<book>
<title>Dummy Value</title>
</book>
</genre>
</userContent>
</user>
</userBooks>
I've tried selecting the single nodes and cloning them, then appending the clone to the parent and removing the child that's no longer required. It became very long and convoluted and I couldn't get it to work. There must be a more elegant solution that I am not aware of.
Any help appreciated.
Thanks.
Using XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> userBooks = doc.Descendants("userBooks").ToList();
foreach(XElement userBook in userBooks)
{
userBook.ReplaceWith(userBook.Element("genre"));
}
}
}
}
​
Here's an example for adding a new root element:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
var originalRoot = doc.DocumentElement;
doc.RemoveChild(originalRoot);
var newRoot = doc.CreateElement("userBooks");
doc.AppendChild(newRoot);
newRoot.AppendChild(originalRoot);
doc.Save(Console.Out);
}
}
Try using the same approach for "removing" the userBooks element in the original - remove the element from its parent, but then add all the child nodes (of userBooks) as new child nodes of the original parent of userBooks.

Categories

Resources