xml multiple namespaces in root - c#

I am having trouble generating an xml root. I have to match this structure as the elements of the xml use the prefixs throughout.
<ShipmentReceiptNotification
xmlns="urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02"
xmlns:dacc="urn:rosettanet:specification:domain:Procurement:AccountClassification:xsd:codelist:01.03"
xmlns:dbpq="urn:rosettanet:specification:domain:Procurement:BookPriceQualifier:xsd:codelist:01.04"
xmlns:dccc="urn:rosettanet:specification:domain:Procurement:CreditCardClassification:xsd:codelist:01.03"
xmlns:dcrt="urn:rosettanet:specification:domain:Procurement:CustomerType:xsd:codelist:01.03"
..\..\XML\Interchange\ShipmentReceiptNotification_02_02.xsd">
if I do something like
XmlNode ShipmentReceiptNotification0Node = xmlDoc.CreateElement("ShipmentReceiptNotification", "xmlns=\"urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02\"xmlns:dacc=\"urn:rosettanet:specification:domain:Procurement:AccountClassification:xsd:codelist:01.03\"");
I get
-ShipmentReceiptNotification
xmlns="xmlns="urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02"xmlns:dacc=&
quot;urn:rosettanet:specification:domain:Procurement:AccountClassification:xsd:codelist:01.03"">

The second argument of CreateElement accepts the URI of the namespace that the element being created, that is ShipmentReceiptNotification, belongs to. Not the whole bunch of xmlns attributes. This code:
XmlElement e = xmlDoc.CreateElement(
"ShipmentReceiptNotification",
"urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02");
Produces this XML:
<ShipmentReceiptNotification
xmlns="urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02" />
To produce what you want, you need to add attributes to the element. Like this:
XmlElement e = xmlDoc.CreateElement("ShipmentReceiptNotification");
e.SetAttribute("xmlns", "urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02");
e.SetAttribute("xmlns:dacc", "urn:rosettanet:specification:domain:Procurement:AccountClassification:xsd:codelist:01.03");
Produces this XML:
<ShipmentReceiptNotification
xmlns="urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02"
xmlns:dacc="urn:rosettanet:specification:domain:Procurement:AccountClassification:xsd:codelist:01.03" />
Note that this is the “manual” way. You should play with XmlNamespaceManager to do it “right”. However, that may be a bit more complex task which need not be necessary for your scenario.

Related

XmlDocument Searching for namespace returns children

I am trying to convert nodes that have a namespace declaration over to use a prefix instead. My first stab at it was to just use xslt to transform the xml, but I started looking at doing it with the XmlDocument class and using the SelectNodes() method. The issue I am seeing is when I try to select nodes that have a namespace, it selects that node AND its children. I assume this is because it is selecting the node which contains children.
<foo xmlns="some url">
<child>child</child>
</foo>
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xmlstring);
var query = xdoc.SelectNodes("//*[namespace-uri()='some url']");
the query variable will return <foo> and <child> nodes, so when I loop through the nodes and change it to use the prefix, I get the following result.
<prefix:foo>
<prefix:child></prefix:child>
</prefix:foo>
Is there a way to just return just the <foo> node in this case? Is it better to also use xslt to transform it?
I didnt think you could change a namespace or prefix when using XDocument and XElement, so thats why I used XmlDocument.
Update
The result id want would be the prefix only on the node where the declaration was. This is valid xml correct or does the prefix need to be on the children as well to be valid?
<prefix:foo>
<child>child</child>
</prefix:foo>
In the XDM data model used by XPath and XSLT, there is no distinction between
<foo xmlns="some url">
<child>child</child>
</foo>
and
<foo xmlns="some url">
<child xmlns="some url">child</child>
</foo>
Logically the namespace is present on both element nodes, and its omission from the child in the lexical serialization is treated as a convenient abbreviation.
So yes, if you search for things having this namespace, you will get both elements.
Now, what are your requirements? I'm not convinced you fully understand them yourself, because the desired output you have shown is not actually well-formed (the namespace prefix is not declared). In your input, the two elements are in the same namespace; in the output, you seem to want them to be in different namespaces. If you want to process them differently, then you're going to have to use something other than the namespace to discriminate between them.
Remember that in XDM, it's the name of the node that matters, not the namespace declarations or prefixes; those are just ornamental. The name of the node is the combination of its local name and its namespace URI. You've described your requirement in terms of prefixes, but it's namespaces that actually matter.

How to access an XML element in a single go?

I have an XML string like below:
<root>
<Test1>
<Result time="2">ProperEnding</Result>
</Test1>
<Test2></Test2>
I have to operate on these elements. Most of the time the elements are unique within their parent element. I am using XDocument. I can remember that there is a way to access an element like this.
XNode resultTest1 = GetNodes("/root//Test1//result")
But I forgot it. It is possible to access the same using linq:
doc.root.Elements.etc.etc.
But I want it using a single string as shown above. Can anybody say how to make it?
Descendants() will skip any number level of intermediate nodes, e.g. this will skip over root and Test1:
doc.Decendants("Result")
Also note that you can use XPath with Linq2Xml as well, e.g. XPathSelectElements
doc.XPathSelectElements("/root/Test1/Result");
You can skip intermediate levels of the hierarchy with // (or use // at the start of the xpath string to skip the root)
"/root//Result"
One caveat - Xml is case sensitive , so Result and result are not the same element.
The string you're referring to ("/root//Test1//result") is an XPath expression.
You can use it with LINQ to XML classes (like XDocument) using XPathEvaluate, XPathSelectElement, and XPathSelectElements extension methods.
You can find more info about these methods on MSDN: http://msdn.microsoft.com/en-us/library/vstudio/system.xml.xpath.extensions_methods(v=vs.90).aspx
To make them work, you need using System.Xml.XPath at the top of your file and System.Xml.Linq.dll assembly referenced (which is probably already there).
You can try to load your xml using XDocument:
// loads xml file with root element
XDocument xml = XDocument.Load("filename.xml");
Now you can append LINQ statements to your xml variable like this:
var retrieveSomeSpecificDataLikeListOfElementsAsAnonymousObjects = xml.Descendants("parentNodeName").Select(node => new { SomeSpecialValueYouWant = node.Element("elementNameUnderParentNode").Value }).ToList();
You can mix and do whatever you want - above is just an example.
Is this what you looking?
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("YourXML");
XmlNodeList xmlNodes = xmlDocument.SelectNodes("/root/Test1/result");

Difference between XPathEvaluate on XElement or XDocument?

Somewhere in a C# program, I need to get an attribute value from an xml structure. I can reach this xml structure directly as an XElement and have a simple xpath string to get the attribute. However, using XPathEvaluate, I get an empty array most of the time. (Yes, sometimes, the attribute is returned, but mostly it isn't... for the exact same XElement and xpath string...)
However, if I first convert the xml to string and reparse it as an XDocument, I do always get the attribute back. Can somebody explain this behavior ? (Am using .NET 3.5)
Code that mostly returns an empty IEnumerable:
string xpath = "/exampleRoot/exampleSection[#name='test']/#value";
XElement myXelement = RetrieveXElement();
((IEnumerable)myXElement.XPathEvaluate(xpath)).Cast<XAttribute>().FirstOrDefault().Value;
Code that does always work (I get my attribute value):
string xpath = "/exampleRoot/exampleSection[#name='test']/#value";
string myXml = RetrieveXElement().ToString();
XDocument xdoc = XDocument.Parse(myXml);
((IEnumerable)xdoc.XPathEvaluate(xpath)).Cast<XAttribute>().FirstOrDefault().Value;
With the test xml:
<exampleRoot>
<exampleSection name="test" value="2" />
<exampleSection name="test2" value="2" />
</exampleRoot>
By suggestion related to a surrounding root, I did some 'dry tests' in a test program, using the same xml structure (txtbxXml and txtbxXpath representing the xml and xpath expression described above):
// 1. XDocument Trial:
((IEnumerable)XDocument.Parse(txtbxXml.Text).XPathEvaluate(txtbxXPath.Text)).Cast<XAttribute>().FirstOrDefault().Value.ToString();
// 2. XElement trial:
((IEnumerable)XElement.Parse(txtbxXml.Text).XPathEvaluate(txtbxXPath.Text)).Cast<XAttribute>().FirstOrDefault().Value.ToString();
// 3. XElement originating from other root:
((IEnumerable)(new XElement("otherRoot", XElement.Parse(txtbxXml.Text)).Element("exampleRoot")).XPathEvaluate(txtbxXPath.Text)).Cast<XAttribute>().FirstOrDefault().Value.ToString();
Result : case 1 and 3 produce the correct result, while case 2 throws a nullref exception.
If case 3 would fail and case 2 succeed, it would have made some sense to me, but now I don't get it...
The problem is that the XPath expression is starting with the children of the specified node. If you start with an XDocument, the root element is the child node. If you start with an XElement representing your exampleRoot node, then the children are the two exampleSection nodes.
If you change your XPath expression to "/exampleSection[#name='test']/#value", it will work from the element. If you change it to "//exampleSection[#name='test']/#value", it will work from both the XElement and the XDocument.

How can I update XML element or attribute which its name contains ":" special character

I have XML file and the elements/attributes names have ":" character, how I can update its vales?
<?xml version="1.0" encoding="utf-8"?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.3"?>
<my:ECR my:NoOfAutho="16" my:hideDetails="0" my:Type="ECR" my:NoOfVBUCMApprovales="10" >
<my:ECRNo>148</my:ECRNo>
<my:Stage>Approved</my:Stage>
<my:Details>
<my:ReasonForCR>Reason For CR</my:ReasonForCR>
<my:AreaAffected_Publications_VBUCM>false</my:AreaAffected_Publications_VBUCM>
<my:AreaAffected_Engineering>true</my:AreaAffected_Engineering>
<my:AreaAffected_Production>false</my:AreaAffected_Production>
<my:AreaAffected_CustomerSupport>true</my:AreaAffected_CustomerSupport>
<my:AreaAffected_VBUCMTest>false</my:AreaAffected_VBUCMTest>
</my:AreaAffectedVB_UCM>
Your XML sample is invalid as shown. The my prefix is not defined in the XML.
If your XML contained xmlns:my="schemas.microsoft.com/office/infopath/2003/myXSD/…" then the XML would at least have some hope of being valid.
For manipulating XML with namespaces in .NET code, consider using Linq XDocument instead of XmlDocument. I have found Linq's XNamespace and XName types to be much, much easier to use with the XDocument family of classes than the old style XmlDocument's rather clunky handling of namespaces.
Change your XML to add the xmlns:my attribute to the root element:
<my:MNO xmlns:my="schemas.microsoft.com/office/infopath/2003/myXSD/…" my:NoOfAutho="16" etc... >
In your C# code, add a reference to the Linq stuff to the top of your source file:
using System.Xml.Linq;
Then use code like this (not checked, may contain syntax typos) to load the xml and access the element:
XNamespace ns = "schemas.microsoft.com/office/infopath/2003/myXSD/…";
XName MNO_Name = ns + "MNO";
XDocument doc = XDocument.Load(path2);
XElement MNO_Element = doc.Root.Descendants(MNO_Name).Single();
You can then read or modify the properties, attributes, and children of the MNO element.
To read the value of <MNO>100</MNO>, use MNO_Element.Value.
To write a new value to the element, assign to the value property: MNO_Element.Value = "120";
.Single() asserts that there is exactly one node that matches the selection criteria, similar to the .SelectSingleNode() function of XmlDocument.
As you can see from this code, the name of the "my" namespace prefix in the XML document is immaterial to the code that processes the XML - it's the URI that the "my" prefix represents that is what is important. The prefix is just shorthand so the the XML writer doesn't have to write long and laborious URIs everywhere.
Writing your XML processing code to be agnostic of the XML namespace prefix is very important because the prefix name can (and will) vary from one XML doc to the next, but the namespace URI will be the same.
I don't understand what you mean by "how can I update its values", but it will likely help if you understand that these are XML namespaces.
I.E., my:ECRNo has a simple element name of ECRNo with a namespace prefix of my, which maps to a URN or a URL - which should be declared with a xmlns:my=... within the XML (either where it is declared, at a parent, or in the XML root element) - but isn't shown in the XML sample you provided here.
To update this using XmlNode, you need to use the overloaded SelectSingleNode method that accepts a XmlNamespaceManager as the 2nd argument. You then need to all the .AddNamespace method on the namespace manager to register the my prefix. This is detailed at http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.selectsinglenode%28v=VS.90%29.aspx .
Colons are not valid characters in xml elements / attributes. They are namespaces.
Your line
<my:ECR my:NoOfAutho="16" my:hideDetails="0" my:Type="ECR" my:NoOfVBUCMApprovales="10" >
Properly references the my namespace already, so you should just be able to do this:
<my:ECR NoOfAutho="16" hideDetails="0" Type="ECR" NoOfVBUCMApprovales="10" >
And you should be fine?
You will also have to remove the my: from other places in the file particularly closing tags
</ReasonForCR>

Parsing XML document with XPath, C#

So I'm trying to parse the following XML document with C#, using System.XML:
<root xmlns:n="http://www.w3.org/TR/html4/">
<n:node>
<n:node>
data
</n:node>
</n:node>
<n:node>
<n:node>
data
</n:node>
</n:node>
</root>
Every treatise of XPath with namespaces tells me to do the following:
XmlNamespaceManager mgr = new XmlNamespaceManager(xmlDoc.NameTable);
mgr.AddNamespace("n", "http://www.w3.org/1999/XSL/Transform");
And after I add the code above, the query
xmlDoc.SelectNodes("/root/n:node", mgr);
Runs fine, but returns nothing. The following:
xmlDoc.SelectNodes("/root/node", mgr);
returns two nodes if I modify the XML file and remove the namespaces, so it seems everything else is set up correctly. Any idea why it work doesn't with namespaces?
Thanks alot!
As stated, it's the URI of the namespace that's important, not the prefix.
Given your xml you could use the following:
mgr.AddNamespace( "someOtherPrefix", "http://www.w3.org/TR/html4/" );
var nodes = xmlDoc.SelectNodes( "/root/someOtherPrefix:node", mgr );
This will give you the data you want. Once you grasp this concept it becomes easier, especially when you get to default namespaces (no prefix in source xml), since you instantly know you can assign a prefix to each URI and strongly reference any part of the document you like.
The URI you specified in your AddNamespace method doesn't match the one in the xmlns declaration.
If you declare prefix "n" to represent the namespace "http://www.w3.org/1999/XSL/Transform", then the nodes won't match when you do your query. This is because, in your document, the prefix "n" refers to the namespace "http://www.w3.org/TR/html4/".
Try doing mgr.AddNamespace("n", "http://www.w3.org/TR/html4/"); instead.

Categories

Resources