Use XmlReader to just get children - c#

I have this XML:
<root>
<row>
<data1>Data</data1>
<data2>Data</data2>
<data3>Data
<subdata>SubData</subdata>
</data3>
</row>
</root>
and I want to use an XmlReader to read only the <dataX> elements, without knowing the exact name of dataX. I found the ReadToNextSibling method, but it needs a name, which I do not know.

You can use the following XPath to filter the elements to just the <dataN> elements:
/root/row/*[substring(name(), 0,5) = 'data']
/root/row/*[starts-with(name(),'data')]
However, XmlReader cannot use xPath nor wildcards for element names in any of its navigation methods such as ReadToNextSibling
You could use XPathReader, which can be instantiated from an XmlReader object, as follows:
XPathReader xpr = new XPathReader("MyXml.xml", "/root/row/*[substring(name(), 0,5) = 'data']");
while (xpr.ReadUntilMatch()) {
Console.WriteLine(xpr.ReadString());
}
(You can download XPathReader from Microsoft here: https://www.microsoft.com/en-us/download/details.aspx?id=22677)
If you don't like the idea of XPathReader, you can do the following with XmlDocument:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xml);
XmlNode root = xDoc.DocumentElement;
foreach (XmlNode item in root.SelectNodes(#"/root/row/*[substring(name(),0,5) = 'data']"))
{
Console.WriteLine(item.Value);
}
EDIT
A better XPath would actually be:
/root/row/*[starts-with(name(),'data')]

Related

C# How to insert XPathnavigator into XPathnavigator

Let's say I have the following XML file
<root></root>
and the following file
<child>
<more-childeren> </more-childeren>
</child>
How do I insert the second file into the first file to create the following file:
<root>
<child>
<more-childeren> </more-childeren>
</child>
</root>
I am receiving the second file as a XPathNavigator. What would be the fastest way to insert the XPathNavigator into the XML file?
If you work with XPathNavigators over editable tree structures like XmlDocument/XmlNode then use the AppendChild method taking an XPathNavigator https://learn.microsoft.com/en-us/dotnet/api/system.xml.xpath.xpathnavigator.appendchild?view=net-5.0#System_Xml_XPath_XPathNavigator_AppendChild_System_Xml_XPath_XPathNavigator_. That is at least the most convenient and API supported way, "the fastest" is a different criteria you would need to test.
A simple example working for me with .NET framework is
XmlDocument doc = new XmlDocument();
doc.LoadXml(#"<root></root>");
XPathNavigator nav = doc.DocumentElement.CreateNavigator();
XPathDocument doc2;
using (XmlReader xr = XmlReader.Create(new StringReader(#"<child>
<more-childeren> </more-childeren>
</child>")))
{
doc2 = new XPathDocument(xr);
}
XPathNavigator nav2 = doc2.CreateNavigator();
nav2.MoveToFirstChild();
nav.AppendChild(nav2);
doc.Save(Console.Out);
The call nav2.MoveToFirstChild(); seems to be crucial to not get the exception you mention in the comments.
Try following xml linq :
string xml1 = "<root></root>";
string xml2 = "<child><more-childeren> </more-childeren></child>";
XDocument doc1 = XDocument.Parse(xml1);
XElement root = doc1.Root;
root.Add(XElement.Parse(xml2));

Extracting objects from xml that uses node prefixes using xpath

I have the following XML in a file called C:\temp\config.xml:
<?xml version="1.0" encoding="utf-8"?>
<dvp:systemConfig xmlns:devop="urn:foo.com:sys:rules">
<dvp:map id="rootFolderMap">
<dvp:entry key="anything" value="folder1"/>
<dvp:entry key="config" value="folder2"/>
<dvp:defaultValue value="folder1" />
</dvp:map>
<dvp:map id="folderMappings">
<dvp:entry key="SYS" value="System" />
<dvp:entry key="OPS" value="Operations" />
<dvp:entry key="DEV" value="Development" />
<dvp:defaultValue value="Miscellaneous" />
</dvp:map>
</dvp:systemConfig>
I am now trying to write C# code that will extract values from this XML. In particular, the list of value properties in the nodes.
List<string> folderNames = new List<string>
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\temp\config.xml");
XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
XmlNodeList xmlNodeList =
doc.SelectNodes("/systemConfig/map[#id='folderMappings']/entry",nsMgr);
foreach (XmlNode xmlNode in xmlNodeList)
{
// I'm hoping xmlNodeList now contains a list of <dvp:entry> nodes.
string folderName = xmlNode.Attributes["value"].ToString().Trim();
folderNames.Add(folderName);
}
However, this code returns an empty xmlNodeList, so the final loop has no items.
I'm not sure how to use the XmlNamespaceManager to pick up the "dvp" prefix, or whether it figures this out when it loads the XmlDocument from the XML on disk.
Can anyone give me some guidance how one goes about using xpath to retrieve values for xml with prefixed nodes? Unfortunately I do not have the power to change the format of the XML so I have to work with the xml I'm provided with.
thanks heaps,
David.
you can ignore the namespace using local-name()
XmlNodeList xmlNodeList =
doc.SelectNodes("/*[local-name()='systemConfig']/*[local-name()='map'][#id='folderMappings']");

Get childNode in childNode in XMLDocument

I have XML response from service and I need to get value of tag that exist in child node that this child node is a child node.
For example: This is an example of xml.
<ashrait>
<response>
<command>inquire</command>
<inquire>
<row>
<ResponseCode>000</ResponseCode>
<ResponseText>
Permitted.
</ResponseText>
<ResponseXML>
<ashrait>
<response>
<message>Permitted .</message>
<userMessage>Permitted .</userMessage>
</response>
</ashrait>
</ResponseXML>
</row>
</inquire>
</response>
</ashrait>
I need the the value in tag "userMessage" that exists in tag "ResponseXML".
I know that to get the node of "ResponseXML" I need for those lines:
var doc = new XmlDocument();
doc.LoadXml(responseFile);
ChildNode result = doc.GetElementsByTagName("ResponseXML")[0];
But how i get the tag userMessage in childNode "ResponseXML"?
Thanks
UPDATE:
I figured out how to do it.
Search for all the children with the tag and choose the order they want.
Create a model for your XML, load that model using XmlSerializer.
Check this Microsoft Docs.
https://learn.microsoft.com/en-us/dotnet/standard/serialization/examples-of-xml-serialization
Use
SelectNodes method: https://msdn.microsoft.com/en-us/library/hcebdtae(v=vs.110).aspx
or SelectSingleNode method: https://msdn.microsoft.com/en-us/library/system.xml.xmlnode.selectsinglenode(v=vs.110).aspx
var doc = new XmlDocument();
doc.LoadXml(Xml);
XmlNode xn = doc.SelectSingleNode("//ashrait//inquire//row//ResponseXML//message");
var innerText = xn.InnerText;

Getting XML node using XPath returns null

I am trying to get a node in a simple XML, but no matter what I try I always get null.
I am guessing that the issue is the namespace.
I am simply trying to get the value of the ID element, 331377697.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("E:\\0323.xml");
XmlNamespaceManager manager = new XmlNamespaceManager(xmlDoc.NameTable);
manager.AddNamespace("cac", "http://xxxxxx Some URL XXXX");
manager.AddNamespace("cbc", "http://xxxxxx Some URL XXXX");
string query = "/StandardBusinessDocument/Invoice/cbc:ID";
XmlNode xmlNode = xmlDoc.SelectSingleNode(query, manager);
The XML:
<StandardBusinessDocument xmlns="http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader">
<StandardBusinessDocumentHeader>
...
</StandardBusinessDocumentHeader>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ccts="urn:un:unece:uncefact:documentation:2"
...
</Invoice>
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:CustomizationID>1234</cbc:CustomizationID>
<cbc:ProfileID>1234564</cbc:ProfileID>
<cbc:ID>331377697</cbc:ID>
<cbc:IssueDate>2017-03-23</cbc:IssueDate>
<cbc:InvoiceTypeCode listID="UNCL1001">380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode listID="ISO4217">NOK</cbc:DocumentCurrencyCode>
<cac:OrderReference>
<cbc:ID>146136</cbc:ID>
</cac:OrderReference>
...
You must specify the exact namespaces.
Also you have to specify namespace prefixes for all elements in the XPath.
manager.AddNamespace("nsDoc", "http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader");
manager.AddNamespace("nsInvoice", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2");
manager.AddNamespace("cbc", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2");
string query = "/nsDoc:StandardBusinessDocument/nsInvoice:Invoice/cbc:ID";
It's supposed to work.

Read first root node from XML

I work with three kinds of XML files :
Type A:
<?xml version="1.0" encoding="UTF-8"?>
<nfeProc versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
</nfeProc>
Tyepe B:
<?xml version="1.0" encoding="UTF-8"?>
<cancCTe xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
</cancCTe>
Type C:]
<?xml version="1.0" encoding="UTF-8"?>
<cteProc xmlns="http://www.portalfiscal.inf.br/cte" versao="1.04">
</cteProc>
I have try with this code to read the first node :
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
XmlNodeList ml = xmlDoc.GetElementsByTagName("*");
XmlElement root = xmlDoc.DocumentElement;
exti = root.ToString();
but dont return anything i want to read the first node , need to know if the file is nfeProc ,canCTE or cteProc
The second question is how i get the value from "value" in the same tag???
Thanks
From this post:
//Root node is the DocumentElement property of XmlDocument
XmlElement root = xmlDoc.DocumentElement
//If you only have the node, you can get the root node by
XmlElement root = xmlNode.OwnerDocument.DocumentElement
I would suggest using XPath. Here's an example where I read in the XML content from a locally stored string and select whatever the first node under the root is:
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(xml));
XmlNode node = doc.SelectSingleNode("(/*)");
If you aren't required to use the XmlDocument stuff, then Linq is your friend.
XDocument doc = XDocument.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
XElement first = doc.GetDescendants().FirstOrDefault();
if(first != null)
{
//first.Name will be either nfeProc, canCTE or cteProc.
}
Working with Linq to XML is the newest and most powerful way of working with XML in .NET and offers you a lot more power and flexibility than things like XmlDocument and XmlNode.
Getting the root node is very simple:
XDocument doc = XDocument.Load(#"C:\crruopto\135120068964590_v01.04-procCTe.xml");
Console.WriteLine(doc.Root.Name.ToString());
Once you have constructed an XDocument you don't need to use any LINQ querying or special checking. You simply pull the Root property from the XDocument.
Thanks i have solved this way the first part
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(nomear);
XmlNodeList ml = xmlDoc.GetElementsByTagName("*");
XmlNode primer = xmlDoc.DocumentElement;
exti = primer.Name;
First, to be clear, you're asking about the root element, not the root node.
You can use an XmlReader to avoid having to load large documents completely into memory. See my answer to a how to find the root element at https://stackoverflow.com/a/60642354/1307074.
Second, once the reader is referencing the element, you can use the reader's Name property to get the qualified tag name of the element. You can get the value as a string using the Value property.

Categories

Resources