I have some code to replace the root node name of an XML document while retaining its namespace.
XmlDocument doc = new XmlDocument();
Stream inStream = inmsg.BodyPart.GetOriginalDataStream();
doc.Load(inStream);
XmlNode root = doc.DocumentElement;
XmlNode replacement = doc.SelectSingleNode("/*/*[1]");
XmlNode newRoot = doc.CreateElement(replacement.Name);
XmlAttribute xmlns = (XmlAttribute)root.Attributes["xmlns"].Clone();
newRoot.Attributes.Append(xmlns);
newRoot.InnerXml = root.InnerXml; //the problem is here!
doc.ReplaceChild(newRoot, root);
With a document that begins like this:
<OLD_ROOT xmlns="http://my.xml.namespace">
<NEW_ROOT>
It results in:
<NEW_ROOT xmlns="http://my.xml.namespace">
<NEW_ROOT xmlns="http://my.xml.namespace">
The second xmlns is because the InnerXml property apparently sets it on the first node of its contents! What can I do to circumvene this, without having to remove it afterwards?
Cannot remove it afterwards:
Tried with the following code
XmlNode first_node = doc.SelectSingleNode("/*/*[1]");
XmlAttribute excess_xmlns = first_node.Attributes["xmlns"];
first_node.Attributes.Remove(excess_xmlns);
But this does not work as xmlns apparently does not exist as an attribute on that node!
Two changes:
Instead of adding an xmlns attribute after you create newRoot, specify the namespace as the second argument in the call to doc.CreateElement. This ensures that the NamespaceURI property is set correctly.
Instead of copying InnerXml (which results in redundant xmlns attributes), use AppendChild to move each child node one at a time. This will likely be more efficient anyway.
Thus:
XmlNode root = doc.DocumentElement;
XmlNode replacement = doc.SelectSingleNode("/*/*[1]");
XmlNode newRoot = doc.CreateElement(replacement.Name, replacement.NamespaceURI);
while (root.ChildNodes.Count > 0)
newRoot.AppendChild(root.FirstChild);
doc.ReplaceChild(newRoot, root);
Related
My problem is that I can't seem to get a list of nodes I need. I have tried multiple solutions and it doesn't seem to work. This is a part of my xml file:
<findItemsByCategoryResponse xmlns="http://www.ebay.com/marketplace/search/v1/services">
<ack>Success</ack>
<version>1.13.0</version>
<timestamp>2016-08-23T07:33:22.497Z</timestamp>
<searchResult count="100">
<item>
<itemId>152210133431</itemId>
<title>...</title>
<globalId>EBAY-GB</globalId>
<primaryCategory>
<categoryId>218</categoryId>
<categoryName>Magic the Gathering</categoryName>
</primaryCategory>
<galleryURL>
http://thumbs4.ebaystatic.com/m/meIDrVqhmbpQMYCxzeUvR9Q/140.jpg
</galleryURL>
<viewItemURL>
http://www.ebay.co.uk/itm/MTG-See-Unwritten-Khans-Tarkir-MYTHIC-MINT-/152210133431
</viewItemURL>
<paymentMethod>PayPal</paymentMethod>
<autoPay>false</autoPay>
<location>London,United Kingdom</location>
<country>GB</country>
<shippingInfo>
<shippingServiceCost currencyId="GBP">1.1</shippingServiceCost>
<shippingType>Flat</shippingType>
<shipToLocations>GB</shipToLocations>
</shippingInfo>
<sellingStatus>
<currentPrice currencyId="GBP">0.5</currentPrice>
<convertedCurrentPrice currencyId="GBP">0.5</convertedCurrentPrice>
<bidCount>0</bidCount>
<sellingState>Active</sellingState>
<timeLeft>P0DT0H19M12S</timeLeft>
</sellingStatus>
<listingInfo>
<bestOfferEnabled>false</bestOfferEnabled>
<buyItNowAvailable>false</buyItNowAvailable>
<startTime>2016-08-18T07:52:34.000Z</startTime>
<endTime>2016-08-23T07:52:34.000Z</endTime>
<listingType>Auction</listingType>
<gift>false</gift>
</listingInfo>
<condition>
<conditionId>1000</conditionId>
<conditionDisplayName>New</conditionDisplayName>
</condition>
<isMultiVariationListing>false</isMultiVariationListing>
<topRatedListing>false</topRatedListing>
</item>
</searchResult>
<paginationOutput>...</paginationOutput>
<itemSearchURL>...</itemSearchURL>
</findItemsByCategoryResponse>
Normally there are 100 item nodes in this xml file, I just shorted the file. I tried to get the ''item'' nodes and everything inside in a XmlNodeList, but when I debug it says it contains 0 items. After this I want to retrieve some data, as you can see in the code. Note: I tried a lot of xpath values!
Here is the code I used:
XmlDocument xmlText = new XmlDocument();
xmlText.Load(basicUrl);
XmlElement root = xmlText.DocumentElement;
XmlNodeList xnList = root.SelectNodes("//item");
foreach(XmlNode item in xnList)
{
string title = item["title"].InnerText;
Console.WriteLine(title);
}
XPath Expressions are always namespace aware (if the Element has a Namespace then the XPath must reference it by namespace). Also, in XPath expressions, the 'default' namespace is always in the URI "". In your case you should do something like this :
XmlDocument xmlText = new XmlDocument();
xmlText.Load(yourXml);
XmlElement root = xmlText.DocumentElement;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlText.NameTable);
nsmgr.AddNamespace("t", "http://www.ebay.com/marketplace/search/v1/services");
XmlNodeList xnList = xmlText.SelectNodes("//t:item", nsmgr);
foreach (XmlNode item in xnList)
{
string title = item["title"].InnerText;
Console.WriteLine(title);
}
SelectNodes with namespace :
https://msdn.microsoft.com/en-US/library/4bektfx9(v=vs.110).aspx
I am creating XML document by reading some objects and adding them to proper place (inside xml tree structure). To be able to add it to proper place I need parent XmlNode so I could call parentNode.AppendChild(node);
How can I get XmlNode object if I know value of one of its attributes?
XmlDocument dom = new XmlDocument();
XmlNode parentNode = null;
XmlNode node = dom.CreateElement(item.Title); //item is object that I am writing to xml
XmlAttribute nodeTcmUri = dom.CreateAttribute("tcmUri");
nodeTcmUri.Value = item.Id.ToString();
node.Attributes.Append(nodeTcmUri);
parentNode = ??? - how to get XML node if I know its "tcmUri" attribute value (it is unique value, no other node has same "tcmUri" attribute value)
You can do this using SelectSingleNode function and xpath query as below
XmlNode parentNode = dom.SelectSingleNode("descendant::yournodename[#tcmUri='" + item.Id.ToString() + "']");
Where yournodename has to be replaced with the node name of the parent elements
Try this
XmlDocument doc = new XmlDocument();
doc.LoadXml(content);
XmlNodeList list = doc.SelectNodes("mynode");
foreach (XmlNode item in list)
{
if (item.Attributes["tcmUri"].Value == some_value)
{
// do what you want, item is the element you are looking for
}
}
Use following code:
var nodeList = doc.SelectNodes("<Node Name>[#tcmUri = \"<Value>\"]");
if(list.Count>0)
parentNode = list[0];
Replace <Node Name> with the node name which you want to make the parent node.
Replace the <Value> with the value of tcmUri attribute of the Node which you want to make the parent node.
XPath is your friend :
string xpath = String.Format("//parentTag[#tcmUri='{0}']", "tcmUriValueHere");
//or in case parent node name (parentTag) may varies
//you can use XPath wildcard:
//string xpath = String.Format("//*[#tcmUri='{0}']", "tcmUriValueHere");
parentNode = dom.SelectSingleNode(xpath)
Here is what I have attempted:
Creating elements:
XmlNode xHeader = xDoc.CreateElement("Customer");
XmlNode xCustomerID = xDoc.CreateElement("Customer_ID", strListName);
XmlNode xName = xDoc.CreateElement("Full_Name");
XmlNode xEmail = xDoc.CreateElement("Email");
XmlNode xHomeAddress = xDoc.CreateElement("Home_Address");
XmlNode xMobileNumber = xDoc.CreateElement("Mobile_Number");
Appending nodes to document.
xDoc.DocumentElement.AppendChild(xHeader);
xHeader.AppendChild(xCustomerID);
xCustomerID.AppendChild(xEmail);
xCustomerID.AppendChild(xHomeAddress);
xCustomerID.AppendChild(xMobileNumber);
This is what the is generated in the XML. http://pastebin.com/dNs8Ueiw
I want there to be no xmlns = "" in the child nodes of Customer_ID.
If you want XML of:
<Customer_ID xmlns="a">
<Email>
</Email>
<Home_Address>
</Home_Address>
<Mobile_Number>
</Mobile_Number>
</Customer_ID>
... then you need to make sure your Email, Home_Address and Mobile_Number elements are all in the same namespace as your Customer_ID element:
XmlNode xCustomerID = xDoc.CreateElement("Customer_ID", strListName);
XmlNode xEmail = xDoc.CreateElement("Email", strListName);
XmlNode xHomeAddress = xDoc.CreateElement("Home_Address", strListName);
XmlNode xMobileNumber = xDoc.CreateElement("Mobile_Number", strListName);
Basically you're seeing the result of namespace defaulting - unless an xmlns=... is specified for an element, it inherits the namespace of its parent.
(Also note that if you can, you should use LINQ to XML - it's a much more pleasant XML API, with nicer namespace handling.)
I need to get node value from xml. The xml has namespace.
I have the following code
string xml =
"<file xmlns=\"SFAKT\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<document>test</document>" +
"</file>";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDocument.NameTable);
ns.AddNamespace("sf", "SFAKT");
XmlNode node = xmlDocument.SelectSingleNode("sf:file/document");
But node = null
Can you tell me where is the error in my code?
You need to use the overloaded SelectSingleNode method and pass in the XmlNamespaceManager. Also, you need the sf prefix for the document node.
Pull the node out like this:
XmlNode node = xmlDocument.SelectSingleNode("sf:file/sf:document", ns);
What's the difference using root node to select and using document object to select nodes?
Which way is preferred.
For example,
1.
XmlDocument Doc = new XmlDocument();
Doc.Load(mem);
XmlNodeList nodeList = Doc.SelectNodes(#"//#id");
2.
XmlDocument Doc = new XmlDocument();
Doc.Load(mem);
XmlElement root = Doc.DocumentElement;
XmlNodeList nodeList = root.SelectNodes(#"//#id");
In fact, I never got any differences. And use just
Doc.SelectNodes(#"//#id");
because if document's root exists
bool b = Doc.OuterXml == Doc.DocumentElement.OuterXml; // true
Since XPath's // expression always matches from the document root, the result will be the same whether you start from the document root or from its documentElement.
So I guess you're better off using the shorter Doc.SelectNodes("//#id"); syntax.
The root of an XML document contains its document element at least, but it may also contain processing instructions and comments. For instance, in this XML document:
<!-- This is a child of the root -->
<document_element>
<!-- This is a child of the document element -->
<document_element>
<!-- This is also a child of the root -->
the root has three child nodes, one of which is its top-level element. In this case, this:
XmlNodeList comments = doc.SelectNodes("comment()");
and this:
XmlNodeList comments = doc.DocumentElement.SelectNodes("comment()");
return totally different results.