Reading the xml below shows error in c# - c#

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
<w:body>
<w:customXml w:uri="Sample" w:element="note">
<w:p w:rsidR="00B06944" w:rsidRDefault="0051608D" w:rsidP="000E0B9F">
<w:customXml w:element="to">
<w:r w:rsidR="000E0B9F" w:rsidRPr="00B84BAE">
<w:rPr>
<w:b/>
<w:bCs/>
</w:rPr>
<w:t xml:space="preserve">Saran </w:t>
</w:r>
</w:customXml>
</w:body>
</w:document>
as I want to read the node < w:r >
for this i am writing the below code
XmlDocument doc = new XmlDocument();
doc.Load("\\document.xml");
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
XmlNode node = doc.SelectSingleNode("/w:body/w:customXml/w:r", namespaceManager);
which gives:
The error shown in this line is Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function.
How can I read the Xml

You need to tell namespaceManager about the meaning of the alias w. It sounds like this is redundant (from the file), but it is not the case that the aliases you want for query are necessarily those from the source, since the meaning of the file is identical if I replace all the w aliases in the source document with foo (as long as I also define foo:xmlns to the be same). Or I could use xmlns instead of aliases throughout.
Hence:
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
namespaceManager.Add("w",
"http://schemas.openxmlformats.org/wordprocessingml/2006/main");
XmlNode node = doc.SelectSingleNode("/w:body/w:customXml/w:r", namespaceManager);
This allows your query to succeed identically, regardless of the specific aliases used in the source.

It doesn't give me that error - it gives me an error because the <w:p> tag doesn't have a closing tag, and neither does the outer <w:customXml>. Once those are fixed, the document loads correctly.
There's then the matter of getting the right query. Marc has addressed this in his answer, but personally I'd use the methods in LINQ to XML instead:
var doc = XDocument.Load("test.xml");
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
var element = doc.Root.Element(w + "body")
.Element(w + "customXml")
.Element(w + "p")
.Element(w + "customXml")
.Element(w + "r");
This finds the element correctly. Note that it's not the same as your original XPath even aside from the namespaces - look carefully at your XML; it doesn't have a <w:r> element inside the outer <w:customXml> element.

your xml is not valid. you have no closing w:p tag...

Related

C# Using XPath with XmlDocument - Can't select nodes in a namespace (returning null)

I'm trying to do something which ought to be quite simple but I'm having terrible trouble. I have tried code from multiple similar questions in StackOverflow but to no avail.
I'm trying to get various pieces of information from an ABN lookup with the Australian government. Here is anonymised return XML value:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ABRSearchByABNResponse xmlns="http://abr.business.gov.au/ABRXMLSearch/">
<ABRPayloadSearchResults>
<request>
<identifierSearchRequest>
<authenticationGUID>00000000-0000-0000-0000-000000000000</authenticationGUID>
<identifierType>ABN</identifierType>
<identifierValue>00 000 000 000</identifierValue>
<history>N</history>
</identifierSearchRequest>
</request>
<response>
<usageStatement>The Registrar of the ABR monitors the quality of the information available on this website and updates the information regularly. However, neither the Registrar of the ABR nor the Commonwealth guarantee that the information available through this service (including search results) is accurate, up to date, complete or accept any liability arising from the use of or reliance upon this site.</usageStatement>
<dateRegisterLastUpdated>2017-01-01</dateRegisterLastUpdated>
<dateTimeRetrieved>2017-01-01T00:00:00.2016832+10:00</dateTimeRetrieved>
<businessEntity>
<recordLastUpdatedDate>2017-01-01</recordLastUpdatedDate>
<ABN>
<identifierValue>00000000000</identifierValue>
<isCurrentIndicator>Y</isCurrentIndicator>
<replacedFrom>0001-01-01</replacedFrom>
</ABN>
<entityStatus>
<entityStatusCode>Active</entityStatusCode>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</entityStatus>
<ASICNumber>000000000</ASICNumber>
<entityType>
<entityTypeCode>PRV</entityTypeCode>
<entityDescription>Australian Private Company</entityDescription>
</entityType>
<goodsAndServicesTax>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</goodsAndServicesTax>
<mainName>
<organisationName>COMPANY LTD</organisationName>
<effectiveFrom>2017-01-01</effectiveFrom>
</mainName>
<mainBusinessPhysicalAddress>
<stateCode>NSW</stateCode>
<postcode>0000</postcode>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</mainBusinessPhysicalAddress>
</businessEntity>
</response>
</ABRPayloadSearchResults>
</ABRSearchByABNResponse>
</soap:Body>
</soap:Envelope>
so I want to get for example the whole response using xpath="//response" then use various xpath statement within that node to get the <organisationName> ("//mainName/organisationName") and other values.
It should be simple right? Those xpath statements appear to work when testing in Notepad++but I use this code in Visual Studio:
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(ipxml);
XmlNode xnode = xdoc.SelectSingleNode("//response");
XmlNodeList xlist = xdoc.SelectNodes("//mainName/organisationName");
xlist = xdoc.GetElementsByTagName("mainName");
But it always returns null, whatever I put in the xpath I get a null return for the node and 0 count for the list whether I'm selecting something with child nodes, a value or not.
I can get the nodes using GetElementsByTagName() as in the example which returns the correct node, but I wanted to do it 'properly' selecting the proper field using xpath.
I also tried using XElement and Linq but still no luck. Is there something weird about the XML?
I'm sure it must something simple but I've been struggling for ages.
You aren't dealing with the namespaces present in the document. Specifically, the high level element:
<ABRSearchByABNResponse xmlns="http://abr.business.gov.au/ABRXMLSearch/">
places ABRSearchByABNResponse, and all its child elements (unless overridden by another xmlns) into the namespace http://abr.business.gov.au/ABRXMLSearch/. In order to navigate to these nodes (without hacks like GetElementsByTagName or using local-name()), you'll need to register the namespaces with an XmlNamespaceManager, like so. The xmlns aliases don't necessarily need to match those used in the original document, but it's a good convention to do so:
XmlDocument
var xdoc = new XmlDocument();
var ns = new XmlNamespaceManager(xdoc.NameTable);
ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("abr", "http://abr.business.gov.au/ABRXMLSearch/");
xdoc.LoadXml(ipxml);
// NB need to use the overload accepting a namespace
var xresponse = xdoc.SelectSingleNode("//abr:response", ns);
var xlist = xdoc.SelectNodes("//abr:mainName/abr:organisationName", ns);
XDocument
More recently, the powers of LINQ can be harnessed with XDocument, which makes working with namespaces much easier (Descendants finds child nodes at any depth)
var xdoc = XDocument.Parse(ipxml);
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace abr = "http://abr.business.gov.au/ABRXMLSearch/";
var xresponse = xdoc.Descendants(abr + "response");
var xlist = xdoc.Descendants(abr + "organisationName");
XDocument + XPath
You can also resort to using XPath in Linq to Xml, especially for more complicated expressions:
var xdoc = XDocument.Parse(ipxml);
var ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("abr", "http://abr.business.gov.au/ABRXMLSearch/");
var xresponse = xdoc.XPathSelectElement("//abr:response", ns);
var xlist = xdoc.XPathSelectElement("//abr:mainName/abr:organisationName", ns);
You need to call SelectSingleNode and SelectNodes on the DocumentElement. You are calling them on the document itself.
For example:
XmlNode xnode = xdoc.DocumentElement.SelectSingleNode("//response");

Reading child nodes from xml string using C#, LINQ

- <entry xml:base="http://testserver.windows.net/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/"datetime'2015-08-30T00%3A04%3A02.9193525Z'"">
<id>http://testserver.windows.net/Players(PartitionKey='zzz',RowKey='000125')</id>
<category term="testServer.Players" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" title="Players" href="Players(PartitionKey='zzz',RowKey='000125')" />
<title />
<updated>2014-04-30T00:53:42Z</updated>
- <author>
<name />
</author>
- <content type="application/xml">
- <m:properties>
<d:PartitionKey>zzz</d:PartitionKey>
<d:RowKey>000125</d:RowKey>
<d:Timestamp m:type="Edm.DateTime">2014-04-30T00:04:02.9193525Z</d:Timestamp>
<d:Name>Black color</d:Name>
<d:Comments>Test comments</d:Comments>
</m:properties>
</content>
</entry>
How can I read "m:properties" descendants using C# or LINQ.
This xml string is stored in variable of type XElement
You can use combination of XNamespace+"element local name" to reference element in namespace, for example :
XElement myxelement = XElement.Parse("your XML string here");
XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
List<XElement> properties = myxelement.Descendants(m+"properties").ToList();
I think this could show you how to use Linq to XML
read the data from XML Structure using c#
If anything else makes problems, just debug a little, see what you get from L2X operation, and move a step deeper trough data tree.
Using Linq2XML
var xDoc = XDocument.Load(filename);
var dict = xDoc.Descendants("m:properties")
.First()
.Attributes()
.ToDictionary(x => x.Name, x => x.Value);
Setup namespace manager. Note that .net library does not support default namespace, so I added prefix "ns" to default namespace.
use xpath or linq to query xml. Following example uses xpath.
XmlNamespaceManager NamespaceManager = new XmlNamespaceManager(new NameTable());
NamespaceManager.AddNamespace("base", "http://testserver.windows.net/");
NamespaceManager.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
NamespaceManager.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
NamespaceManager.AddNamespace("ns", "http://www.w3.org/2005/Atom"); XDocument doc = XDocument.Parse(XElement);
var properties = doc.XPathSelectElement("/ns:entry/ns:content/m:properties", NamespaceManager);

Read xml descendants collection from xml string [duplicate]

- <entry xml:base="http://testserver.windows.net/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/"datetime'2015-08-30T00%3A04%3A02.9193525Z'"">
<id>http://testserver.windows.net/Players(PartitionKey='zzz',RowKey='000125')</id>
<category term="testServer.Players" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" title="Players" href="Players(PartitionKey='zzz',RowKey='000125')" />
<title />
<updated>2014-04-30T00:53:42Z</updated>
- <author>
<name />
</author>
- <content type="application/xml">
- <m:properties>
<d:PartitionKey>zzz</d:PartitionKey>
<d:RowKey>000125</d:RowKey>
<d:Timestamp m:type="Edm.DateTime">2014-04-30T00:04:02.9193525Z</d:Timestamp>
<d:Name>Black color</d:Name>
<d:Comments>Test comments</d:Comments>
</m:properties>
</content>
</entry>
How can I read "m:properties" descendants using C# or LINQ.
This xml string is stored in variable of type XElement
You can use combination of XNamespace+"element local name" to reference element in namespace, for example :
XElement myxelement = XElement.Parse("your XML string here");
XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
List<XElement> properties = myxelement.Descendants(m+"properties").ToList();
I think this could show you how to use Linq to XML
read the data from XML Structure using c#
If anything else makes problems, just debug a little, see what you get from L2X operation, and move a step deeper trough data tree.
Using Linq2XML
var xDoc = XDocument.Load(filename);
var dict = xDoc.Descendants("m:properties")
.First()
.Attributes()
.ToDictionary(x => x.Name, x => x.Value);
Setup namespace manager. Note that .net library does not support default namespace, so I added prefix "ns" to default namespace.
use xpath or linq to query xml. Following example uses xpath.
XmlNamespaceManager NamespaceManager = new XmlNamespaceManager(new NameTable());
NamespaceManager.AddNamespace("base", "http://testserver.windows.net/");
NamespaceManager.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
NamespaceManager.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
NamespaceManager.AddNamespace("ns", "http://www.w3.org/2005/Atom"); XDocument doc = XDocument.Parse(XElement);
var properties = doc.XPathSelectElement("/ns:entry/ns:content/m:properties", NamespaceManager);

XML Manipulation and Injection

I've the following XML structure which is a part of a xml document:
<p:sp xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
....
....
....
<p:txBody>
<a:bodyPr wrap="square" rtlCol="0" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:noAutofit />
</a:bodyPr>
<a:lstStyle xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" />
<a:p xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:endParaRPr lang="en-US" sz="1200" dirty="0">
<a:solidFill><a:schemeClr val="tx1" />
</a:solidFill>
<a:latin typeface="Verdana" pitchFamily="34" charset="0" />
<a:ea typeface="Verdana" pitchFamily="34" charset="0" />
<a:cs typeface="Verdana" pitchFamily="34" charset="0" /></a:endParaRPr>
</a:p>
</p:txBody>
</p:sp>
I want to select p:txBody and want to inject a:p at the end, I'm using the following code but the txBody i am receiving is not the correct one, because i guess it's extracting that from the doc object not from the shape object:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("p", "http://schemas.openxmlformats.org/presentationml/2006/main");
XmlNode txBody = shape.SelectSingleNode("//p:txBody", nsmgr);
where shape is an XMLNode object having the OuterXml as mentioned above, and using the following code for xml injection:
XmlDocumentFragment fragment = doc.CreateDocumentFragment();
string xml = "valid xml";
fragment.InnerXml = xml;
txBody.Append(fragment);
but the injection is being done in wrong shape.
Your xpath isn't specific enough if it is getting you the wrong node. You need to give it more information to get the node you want. IE what sets the node you want to be different than the other(s)?
As you have it now, you are saying give me the first node you find that has the p namespace and the node name txBody anywhere in the file.
You have shape. but the // in "//p:txBody" says start from the root of the file. If you mean to start from the shape, use .// so like ".//p:txBody"
Use LINQ2XML. It's a complete replacement to other XML APIs.
XElement doc=XElement.Load("yourXML.xml");
XNamespace p = "http://schemas.openxmlformats.org/presentationml/2006/main";
XNamespace s= "http://schemas.openxmlformats.org/drawingml/2006/main";
doc.Elements(p+"txBody").First().Add(new XElement(s+"tagName","value"));

Inserting XML Fragment after last specific node/element

I want to add an XML fragment to the last element to an XML document and I having problems i.e. the error I get is:
"The reference node is not a child of
this node".
So my existing XML document looks like this:
<MAP>
<LAYER name ="My first Layer">
<DATASET name="foo dataset" />
<SYMBOLOGY>
<SYMBOL colour="red" />
</SYMBOLOGY>
</LAYER>
<LAYER name="My second Layer">
<DATASET name="bar dataset" />
<SYMBOLOGY>
<SYMBOL colour="blue" />
</SYMBOLOGY>
</LAYER>
</MAP>
The XML fragment I want to insert after the last LAYER element is:
<LAYER name="My third Layer">
<DATASET name="whatever dataset" />
<SYMBOLOGY>
<SYMBOL colour="yellow" />
</SYMBOLOGY>
</LAYER>
The code I am using is:
XmlDocumentFragment xmlDocFrag = xmlDocument.CreateDocumentFragment();
xmlDocFrag.InnerXml = inputXML; //which is basically the third layer example - see above.
XmlElement rootElement = xmlDocument.DocumentElement;
XmlNode lastLayerNode = rootElement.SelectSingleNode(#"//LAYER[last()]");
rootElement.InsertAfter(xmlDocFrag, lastLayerNode); //error raised here.
Any ideas on what I'm doing wrong here would be much appreciated. My XPath query seems find and it seems to select the correct last layer it just won't insert after it for some bizarre reason.
UPDATE/SOLUTION - How to do this with XPATH
Finally figured it out in XPath - see the code below, I think it was down to basically not selecting the correct parent node in the first place, it's incorrect to select the last LAYER then try and InsertAfter() on this node. Better to select the level above i.e. MAP then AppendChild(). See below:
XmlDocumentFragment xmlDocFrag = xmlDocument.CreateDocumentFragment();
xmlDocFrag.InnerXml = inputXML;
XmlElement mapElement = (XmlElement)xmlDocument.SelectSingleNode(#"//MAP[last()]");
mapElement.AppendChild(xmlDocFrag);
Thanks to all the replies and help too :)
Taking into consideration that you need this to work with Framework 2.0, here's another solution:
string xml = "<map><layer>1</layer><layer>2</layer></map>";
string addMe = "<layer>3</layer>";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlDocumentFragment xmlDocFrag = xmlDocument.CreateDocumentFragment();
xmlDocFrag.InnerXml = addMe;
XmlElement rootElement = xmlDocument.DocumentElement;
rootElement.AppendChild(xmlDocFrag);
This results in:
<map><layer>1</layer><layer>2</layer><layer>3</layer></map>
Things look pretty good, but I would first try to avoid the xpath selection for the last node, and instead just use this:
rootElement.InsertAfter(xmlDocFrag, rootElement.LastChild);
I had similar issue, I used the ImportNode method to solve it
Here is a small example how you can use it to add node from different xml (stored in string) to your example at desired node in xml tree
string xmlstring =#"<tag>.....</tag>"; // holds xml tree to be appended
XmlDocument xml2 = new XmlDocument();
xml2.Load(#"path_of_main_xml");
XmlDocument xml1 = new XmlDocument();
xml1.Load(new StringReader(xmlString));
// get the node you want to import which in this icase is string
XmlNode elem = xml1.DocumentElement;
// use importNode to import it
XmlNode impnode = xml2.ImportNode(elem,true);
// get the node list of all node of particular tag name
XmlNodeList eNode = xml2.GetElementsByTagName("tag_name_of_parent");
eNode[0].AppendChild(impnode); // append new node
// write back the updates to same file
XmlWriter writer = XmlWriter.Create(#"path_of_main_xml");
xml2.Save(writer);

Categories

Resources