How to use XmlDocument.SelectSingleNode() with a default namespace? - c#

I'm trying to pull some values from some customer-supplied XML documents, using XmlDocument.SelectSingleNode().
SelectSingleNode() takes an XPATH string, and very simple searches are failing. E.g. with this:
<?xml version="1.0"?>
<xmlTicket>
<TicketDataSet xmlns="http://tempuri.org/Ticket.xsd">
<Ticket>
<TicketNumber>0123-456-789</TicketNumber>
...
</Ticket>
</TicketDataSet>
</xmlTicket>
XmlDocument.SelectSingleNode("//TicketNumber") returns null.
The problem is clearly the namespace. If I remove the xmlns= from the doc, the XPATHs work fine. If I use namespace neutral XPATH queries, they also work fine:
doc.SelectSingleNode("//*[local-name()='TicketNumber']");
But I can't do the former and I'd rather not do the latter.
I've found examples of how to configure a namespace manager, and tried to work out how to make this work for a default namespace.
This didn't work:
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("", "http://tempuri.org/Ticket.xsd");
var ticketNumberNode = doc.SelectSingleNode("//TicketNumber", nsm);
And this didn't work:
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("default", "http://tempuri.org/Ticket.xsd");
var ticketNumberNode = doc.SelectSingleNode("//default:TicketNumber", nsm);
Any ideas?
===
Note: I updated the XML to show more of the structure. The problem seems to be related to having default namespaces applied to parts of the document, rather than to its entirety.

Try 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);
string ticketNumber = (string)doc.Descendants().Where(x => x.Name.LocalName == "TicketNumber").FirstOrDefault();
}
}
}

The solution is strange but you should specify namespace with prefix in SelectionNamespaces property and specify this prefix in XPath queries. It doesn't matter if this namespace is default namespace with no prefix: you should use one. On the other side, you should add nodes to XML without prefix.
In your example the code should looks like this:
doc.setProperty("SelectionNamespaces", "xmlns:abc='http://tempuri.org/Ticket.xsd'");
doc.selectSingleNode("//abc:TicketNumber");

Related

How to get all descendants with a particular attribute - Linq to Xml

<Grid>
<StringColumn Header="Name"/>
<DateColumn Header="Date"/>
</Grid>
There is probably an existing answer to this question, but I cannot seem to find it.
I need to find all xml elements which have an attribute of "Header"
The name of the element can be different.
How do I do that with Linq to XML?
This should give you the required elements:
XDocument document = ...;
var elementsWithHeader = document.Descendants()
.Where(e => e.Attributes().Any(a => a.Name == "Header"));
Something like this should work:
IEnumerable<XElement> elements =
from el in root.Elements("Grid")
where (string) el.Attribute("Header") != null
select el;
Use this:
var grid = XElement.Parse(#"<Grid>
<StringColumn Header=""Name""/>
<DateColumn Header=""Date""/>
</Grid>");
var elements = grid.XPathSelectElements(".//*[#Header]");
Using xml linq. Code is prints any Grid elements that have children with Header attributes.
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> headers = doc.Descendants("Grid").Where(x => x.Elements().Where(y => y.Attribute("Header") != null).Any()).ToList();
}
}
}

Getting an element tree broken down from an XML

I have an XML and I am try to break down the elements so that it shows the path of every element. I have put an example of the code below and what the output I want to see.
The XML data is as per the below.
<a:Clients>
<a:Client1>
<a:Initial>
<a:FirstName>data</a:FirstName>
<a:LastName>data</a:LastName>
</a:Initial>
<a:FirstName>data</a:FirstName>
<a:MiddleName />
<a:LastName>data</a:LastName>
<a:Title>Mr</a:Title>
<a:MaritalStatus>Divorced</a:MaritalStatus>
<a:MaidenName>data</a:MaidenName>
I want to output it so it shows as
Clients/Client1
Clients/Client1/Initilal
Clients/Client1/Initial/FirstName
etc So I want to get all the elements of my xml to appear in this order for a large XML document. I dont want any data that sits inbetween the tags, Just the element paths.
I have written the below code in vs 2015 - Console Application to try and output it in this order but it is not working correctly. The c# code im using is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
XNamespace ns = "http://www.test.net/author/1.0";
XDocument doc = XDocument.Load("test.xml");
var allElements = doc.Descendants();
foreach (var element in allElements)
Console.WriteLine(allElements);
Console.ReadLine();
}
}
}
The output im currently getting is:
Output
I have looked over Stack Overflow for answers but nothing seems to be exactly what I need and the ones I have tried have not worked either. Not sure if there is an easier way to do this?
Help would be greatly appreciated
I managed to do it in reversed order:
Client1/Clients
So i just entered all into a list and print it in reversed order later to be in the right order.
XDocument doc = XDocument.Load("test.xml");
var allElements = doc.Descendants();
var elemants = doc.Elements();
foreach (var element in allElements)
{
var current = element;
List<string> names = new List<string>();
while (current.Parent != null)
{
names.Add(current.Name.LocalName);
current = current.Parent;
}
names.Add(current.Name.LocalName);
names.Reverse();
foreach(var name in names)
Console.Write(name + "/");
Console.WriteLine();
}
I hope thats what you need.

Read XML with ab:tag format using c#

I am new to xml, c#. I am following this tutorial: http://www.dotnetcurry.com/ShowArticle.aspx?ID=564
But my xml file is little different. The xml that I want to read in my c# code is this: http://api.nextag.com/buyer/synd.jsp?search=ipod&ver=15&token=AQB7dB$kB8ULvbGT&pid=1807
Code I am trying to read this xml is:
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)
{
XElement xelement = XElement.Load("http://api.nextag.com/buyer/synd.jsp?search=ipod&ver=15&token=AQB7dB$kB8ULvbGT&pid=1807");
XNamespace nxtg = "http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-01-11T08:31:30";
IEnumerable<XElement> employees = xelement.Elements();
// Read the entire XML
foreach (var employee in employees)
{
//Console.WriteLine(employee);
//Console.WriteLine(employee.Value);
if (employee.Element(nxtg + "search-category") == null)
continue;
else
Console.WriteLine(employee.Element(nxtg + "search-category").Value);
//Console.WriteLine(employee.Element("EmpId").Value);
}
But no luck. Anyone can help me please.
xelement.Elements() will return direct children of root element. In your case that will be elements nxtg:publisher, nxtg:search-query, nxtg:search-category etc. Thus nxtg:search-category is a direct child of root element, it also will selected as employee. That's why you can't find it in children of employee. You should do following instead:
// keep in mind, you have incorrect namespace value
XNamespace nxtg = "http://namespace.nextag.com/business-objects";
var searchCategory = xelement.Element(nxtg + "search-category");
var node = searchCategory.Element(nxtg + "node");
var displayName = (string)node.Element(nxtg + "display-name");
var value = (int)node.Element(nxtg + "value");

Select XML Nodes by Attribute Value

I have a oracle query to generate below xml result and I like to check only if there is any attribute value inside SUBQUERY and COL_LIST_ITEM nodes not others. If nodes has attribute value than write "it has attribute value" else "no attribute value". I am not sure if my code is correct! Is there any way to do that?
<VIEW xmlns="http://xmlns.oracle.com/ku" version="1.0">
<SCHEMA value1="USER1">USER2</SCHEMA>
<NAME value1="VIEW_TBL_A">VIEW_TBL_B</NAME>
<COL_LIST>
<COL_LIST_ITEM>
</COL_LIST_ITEM>
<COL_LIST_ITEM src="2">
<NAME>FIELD_A2</NAME>
</COL_LIST_ITEM>
</COL_LIST>
<SUBQUERY value1="SELECT FIELD_A1
FROM TBL_A
WHERE FIELD_A1 = 111">SELECT FIELD_A1, FIELD_A2
FROM TBL_A
WHERE FIELD_A1 = 111</SUBQUERY>
</VIEW>
Here is my Code
using (OracleConnection conn1 = new OracleConnection(oradb1))
{
conn1.Open();
using (OracleCommand crtCommand = new OracleCommand("SELECT dbms_metadata.get_sxml('VIEW','VIEW_TBL_A') FROM dual;", conn1))
{
XmlDocument xml = new XmlDocument();
xml.LoadXml(crtCommand.ExecuteScalar().ToString());
}
}
how about an XDocument
XDocument doc = XDocument.Parse(crtCommand.ExecuteScalar().ToString());
XNamespace ns = "http://xmlns.oracle.com/ku";
if (doc.Descendants(ns + "COL_LIST_ITEM").Any(c => c.Attributes().Any()))
Console.WriteLine("COL_LIST has value");
and of course add your other nodes in to check
You should use LINQ to XML to query your XML document:
XDocument document = XDocument.Load(xmlFile);
IEnumerable<XElement> elements =
from element in document.Root.Elements("SUBQUERY")
where element.HasAttributes
select element;
Refer to the article here for more details.
I also ran into a similar problem, while working on a Universal Windows App, and was also very frustrated when VS didn't specify clearly what the problem was. Although after hours of surfing the web, I found the solution. It was simple, but i don't know why VS didn't specify it. Anyways, here it is :-
Add another reference to your project :-
using System.Linq; apart from using System.Xml.Linq;
using System.Linq;
using System.Xml.Linq;
The errors will go & the project will build.
For more help reference this article :-
problem with using refrence to System.Xml.Linq

What is the best way to read an attribute from an xml string in c#

I have the following xml as string:
<cfdi:Comprobante version="3.0"
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv3.xsd"
serie="A"
folio="6"
fecha="2011-07-22T13:51:42"
formaDePago="Pago en una sola exhibición"
sello="XlSJYAxauwYbI"
noCertificado="00001000000101242210"
certificado="YtEQOHw02OGx6E="
condicionesDePago="Paguese a mas tardar el 21/08/2011."
subTotal="123"
Moneda="MXN"
total="123"
tipoDeComprobante="ingreso">
<cfdi:Complemento>
<tfd:TimbreFiscalDigital FechaTimbrado="2011-07-22T13:51:47"
UUID="41C8A54F-4956-1BAD-F2CB-48E8343918FD"
noCertificadoSAT="00001000000102616613"
selloCFD="wrwerewe"
version="1.0"
xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/timbrefiscaldigital/TimbreFiscalDigital.xsd"/>
</cfdi:Complemento>
</cfdi:Comprobante>
I want to read the attribute UUID inside the node tfd:TimbreFiscalDigital so I was wondering how to do this using c#, this might be silly but please understand I'm new in c#.
Note: This xml is inside a string, not in a file (our provider's webservice returns the xml as string, is not our fault)
Note2: I can use Linq, or any other library, that's not a prob
Thanks!!
I wrapped this in a root node declaring the namespaces. I'm also using XPath to query for the node.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
class Program
{
static void Main(string[] args)
{
var doc = #"
<Root xmlns:xsi='http://someuri' xmlns:cfdi='http://someuri2' xmlns:tfd='http://someuri3'>
<cfdi:Comprobante version='3.0'
xsi:schemaLocation='http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv3.xsd'
serie='A'
folio='6'
fecha='2011-07-22T13:51:42'
formaDePago='Pago en una sola exhibición'
sello='XlSJYAxauwYbI'
noCertificado='00001000000101242210'
certificado='YtEQOHw02OGx6E='
condicionesDePago='Paguese a mas tardar el 21/08/2011.'
subTotal='123'
Moneda='MXN'
total='123'
tipoDeComprobante='ingreso'>
<cfdi:Complemento>
<tfd:TimbreFiscalDigital FechaTimbrado='2011-07-22T13:51:47'
UUID='41C8A54F-4956-1BAD-F2CB-48E8343918FD'
noCertificadoSAT='00001000000102616613'
selloCFD='wrwerewe'
version='1.0'
xsi:schemaLocation='http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/timbrefiscaldigital/TimbreFiscalDigital.xsd'/>
</cfdi:Complemento>
</cfdi:Comprobante>
</Root>";
var uuid = XDocument.Parse(doc)
var uuid = XDocument.Parse(doc)
.XPathSelectElement("//*[local-name() = 'TimbreFiscalDigital']")
.Attribute("UUID").Value;
// Work with uuid
Console.Read();
}
}
Because you have namespace prefixes, you'll have to use XNamespace instances to help you reference the elements.
// We use these to establish our namespace prefixes
XNamespace cfdi = #"http://www.sat.gob.mx/cfd/3";
XNamespace tfd = #"http://www.sat.gob.mx/TimbreFiscalDigital";
var xdoc = XDocument.Parse(xml);
// Walk down the XML tree to tfd:TimbreFiscalDigital
var elt = xdoc.Element(cfdi + "Comprobante")
.Element(cfdi + "Complemento")
.Element(tfd + "TimbreFiscalDigital");
// Alternately
// var elt = xdoc.Descendants(tfd + "TimbreFiscalDigital")
// .First();
var uuid = (string)elt.Attribute("UUID");
// You can convert attributes and element values to lots of built-in types
// See the Explicit Conversions for XAttribute and XElement on MSDN
var date = (DateTime)elt.Attribute("FechaTimbrado");
Further reading:
MSDN: XElement Operators
MSDN: XAttribute Operators
I find linq's XDocument and related classes to be particularly simple and straightforward to use:
string uuid = XDocument.Parse(xmlString)
.Descendants("TimbreFiscalDigital")
.Attributes("UUID")
.First()
.Value;

Categories

Resources