How can I convert the following XML
<root xmlns:xyz="do/not/change" xmlns="add/alias">
<name>Test</name>
<xyz:id>100<xyz:id>
</root>
To
<abc:root xmlns:xyz="do/not/change" xmlns:abc="add/alias">
<abc:name>Test</abc:name>
<xyz:id>100<xyz:id>
</abc:root>
Using XDocument in C#
Effectively, I want to add an alias to the second namespace, and add the prefix to all elements that do not already have a prefix.
All the information I can find is how to remove or add a namespace, but nothing about how to add a prefix/alias.
It should be something like:
var xml = #"<root xmlns:xyz='do/not/change' xmlns='add/alias'>
<name>Test</name>
<xyz:id>100</xyz:id>
</root>";
var xdoc = XDocument.Parse(xml);
var xn = xdoc.Root.GetDefaultNamespace();
xdoc.Root.SetAttributeValue(XNamespace.Xmlns + "abc", xn.NamespaceName);
xdoc.Root.Attribute("xmlns").Remove();
foreach (var el in xdoc.Root.DescendantsAndSelf())
{
if (el.Name.Namespace == xn)
{
el.Name = xn + el.Name.LocalName;
}
}
Note that this code will break if there is no default namespace defined.
You can add a check like:
if (xn.NamespaceName == string.Empty) ...
Note 2: technically even attributes can have namespaces, like xyz:myattr="Hello". We are skipping this.
Related
I have a XML file like this:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd">
<ServerConfiguration>
<SecurityPolicies>
<ServerSecurityPolicy>
<SecurityMode>None_1</SecurityMode>
</ServerSecurityPolicy>
</SecurityPolicies>
</ServerConfiguration>
</ApplicationConfiguration>
What I want is to add more node named ServerSecurityPolicy by code.
Then I use this code:
string docaddress = "D:\\abc.xml";
XDocument doc = XDocument.Load(docaddress);
var root = doc.Root;
var these = root.Descendants().Where(p => p.Name.LocalName == "SecurityPolicies");
XElement addelement = new XElement("ServerSecurityPolicy");
addelement.Add(new XElement("SecurityMode", "None_1"));
foreach (var elem in these)
{
elem.Add(addelement);
}
doc.Save(docaddress);
It actually works, but the newly added node is something like this:
<ServerSecurityPolicy xmlns="">
<SecurityMode>None_1</SecurityMode>
</ServerSecurityPolicy>
I don't want the attribute "xmlns", then I try to delete it by something like this:
var these2 = root.Descendants().Where(p => p.Name.LocalName == "ServerSecurityPolicy");
foreach (var elem in these2)
{
label2.Text += elem.Attribute("xmlns").Name.LocalName;
elem.Attribute("xmlns").Remove();
}
The label2.Text shows "xmlnsxmlnsxmlsn..." so that I think the elem.Attribute("xmlns") has pointed to the attributes I want to delete, but the Remove() not work.
Can you suggest me some ways to delete the attribute or add node without attribute?
Thank you
The reason you're getting an empty attribute - which xmlns="" refers to - is because your document's root node belongs to the namespace xsi. When you're adding a node without a namespace - as you're doing in your example - you're not actually binding it to the xsi namespace.
To make your node part of the root namespace, replace
new XElement("ServerSecurityPolicy")
with
new XElement("ServerSecurityPolicy",
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"))
The problem is that the element you want to create is actually in the http://opcfoundation.org/UA/SDK/Configuration.xsd namespace, which is the default for the document because of this part of the root element:
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
You can create an element in that namespace easily:
XNamespace configNs = "http://opcfoundation.org/UA/SDK/Configuration.xsd";
var these = root.Descendants(configNs + "ServerSecurityPolicy");
When you add that to your document, you'll find it won't add the xmlns="" part, which was present because you were adding an element without a namespace.
I'd also suggest using the namespace to find elements too:
XNamespace configNs = "http://opcfoundation.org/UA/SDK/Configuration.xsd";
var these = root.Descendants(configNs + "SecurityPolicies");
That's much cleaner than just using the local name, in my view.
I receive an XML file which has on the root node a xmlns namespace assigned:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message xmlns="http://www.myAddress.com/DataRequest/message/">
<Date>2017/01/01</Date>
</Message>
I do not know how to retrive the Date element using XPath, I tried
var root = xDocument.Root;
var dateElement = root.XPathSelectElement("/Message/Date");
If I remove the namespace from the root xml, then I can retrieve the value using "/Message/Date".
I tried to add xmlns to a XmlNamespaceManager, but I get this error:
Prefix "xmlns" is reserved for use by XML.
How can I get the value?
You should use namespace when you specify element's name. Default namespace is easy to get with XElement.GetDefaultNamespace() method:
var ns = root.GetDefaultNamespace();
var dateElement = (DateTime)root.Element(ns + "Date");
If you want to use XPath:
XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable());
manager.AddNamespace("ns", root.GetDefaultNamespace().ToString());
var dateElement = (DateTime)root.XPathSelectElement("/ns:Message/ns:Date", manager);
I would suggest using LINQ.
Here is a link to the code example:https://msdn.microsoft.com/en-us/library/mt693115.aspx
here is the code:
XElement root = XElement.Load("Message.xml");
IEnumerable<XElement> dateNode=
from el in root.Elements("Date")
select el;
foreach (XElement el in dateNode)
Console.WriteLine(el);
I'm new to xml so I'm not sure if I worded the question correctly, but I will do my best to explain.
Basically, I'm trying to parse an xml response in C# such as the one below:
<Premium>
<TotalPremiumAmount>87</TotalPremiumAmount>
<StandardPremium>87</StandardPremium>
<OptionalPremium>0</OptionalPremium>
<StandardTax>0</StandardTax>
<OptionalTax>0</OptionalTax>
<ExtendedTax>0</ExtendedTax>
<ExtendedPremium>0</ExtendedPremium>
<PromotionalPremium>0</PromotionalPremium>
<FeesPremium>0</FeesPremium>
<FeesTax>0</FeesTax>
<StandardFeesPremium>0</StandardFeesPremium>
<OptionalFeesPremium>0</OptionalFeesPremium>
<Tax>0</Tax>
<StandardPremiumDistribution>
<Travelers>
<Traveler>
<TravelerPremium>42</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
<Traveler>
<TravelerPremium>45</TravelerPremium>
<TravelerTax>0</TravelerTax>
</Traveler>
</Travelers>
</StandardPremiumDistribution>
<PackagePremiumDistribution>
<Packages>
<Package>
<PackageID>20265</PackageID>
<PackageName />
<PackageTypeID>12</PackageTypeID>
<Premium>87</Premium>
<Fees>0</Fees>
<Tax>0</Tax>
<Travelers>
<Traveler>
<TravelerID>0</TravelerID>
<Premium>42</Premium>
<Tax>0</Tax>
</Traveler>
<Traveler>
<TravelerID>1</TravelerID>
<Premium>45</Premium>
<Tax>0</Tax>
</Traveler>
</Travelers>
</Package>
</Packages>
</PackagePremiumDistribution>
</Premium>
I would like to get the value of the (Traveler) Premium. In the case of only one traveler, I have been using an XMLDocument and the 'SelectSingleNode" function. For example I could do something like:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//TravelerPremium").InnerText;
But this wouldn't work when multiple travelers are returned under one plan. For example, I need the premium when TravelerID = 0. How would I go about doing this?
Thanks.
Using XmlDocument:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlResponse);
var premium = xmlDoc.SelectSingleNode("//Premium[../TravelerID = '0']")
You could also iterate through the nodes if multiple could match on this condition like so:
foreach(var premium in xmldoc.SelectNodes("//Premium[../TravelerID = '0']")
{
// do work on each premium node where TravelerID = 0
}
I'd encourage you to look into using LINQ to XML - it's generally easier to work with and will be more performant in most cases. You could even still use XPath expressions, but the following would work:
XDocument xdoc = XDocument.Load(xmlResponse);
var premium = (string)xdoc.Descendants("Traveler").Where(x => (string)x.Element("TravelerID") == "0").Element("Premium");
Assuming your xml looks like that, try something like this:
XmlDocument doc = new XmlDocument();
xmlDoc.Load(xmlResponse);
if (doc.HasChildNodes)
{
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
if (node.Name == "StandardPremiumDistribution")
{
XmlNodeList xnList = node.SelectNodes("//Travelers");
double travelerPremium= xnList.Item(z).FirstChild.InnerText);
}}
Based on this, I think you're gonna do it.
Let's suppose you have a file called XMLFile1.xml with the XML you posted you can iterate through all your TravelerPremium with the following code:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("XMLFile1.xml");
XmlNodeList premiums = xmlDoc.SelectNodes("//TravelerPremium");
foreach(XmlNode node in premiums)
{
MessageBox.Show(node.FirstChild.InnerText);
}
You can also acces the other elements with similar code.
Situation
I'm using XDocument to try and remove an xmlns="" attribute on the first inner node:
<Root xmlns="http://my.namespace">
<Firstelement xmlns="">
<RestOfTheDocument />
</Firstelement>
</Root>
So what I want as a result is:
<Root xmlns="http://my.namespace">
<Firstelement>
<RestOfTheDocument />
</Firstelement>
</Root>
Code
doc = XDocument.Load(XmlReader.Create(inStream));
XElement inner = doc.XPathSelectElement("/*/*[1]");
if (inner != null)
{
inner.Attribute("xmlns").Remove();
}
MemoryStream outStream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(outStream);
doc.Save(writer); // <--- Exception occurs here
Problem
Upon trying to save the document, I get the following exception:
The prefix '' cannot be redefined from '' to 'http://my.namespace' within the same start element tag.
What does this even mean and what can I do to remove that pesky xmlns=""?
Notes
I do want to keep the root node's namespace
I only want that specific xmlns removed, there will be no other xmlns attributes in the document.
Update
I've tried using code inspired from answers on this question:
inner = new XElement(inner.Name.LocalName, inner.Elements());
When debugging, the xmlns attribute is gone from it but I get the same exception.
I think the code below is what you want. You need to put each element into the right namespace, and remove any xmlns='' attributes for the affected elements. The latter part is required as otherwise LINQ to XML basically tries to leave you with an element of
<!-- This would be invalid -->
<Firstelement xmlns="" xmlns="http://my.namespace">
Here's the code:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument doc = XDocument.Load("test.xml");
// All elements with an empty namespace...
foreach (var node in doc.Root.Descendants()
.Where(n => n.Name.NamespaceName == ""))
{
// Remove the xmlns='' attribute. Note the use of
// Attributes rather than Attribute, in case the
// attribute doesn't exist (which it might not if we'd
// created the document "manually" instead of loading
// it from a file.)
node.Attributes("xmlns").Remove();
// Inherit the parent namespace instead
node.Name = node.Parent.Name.Namespace + node.Name.LocalName;
}
Console.WriteLine(doc); // Or doc.Save(...)
}
}
There is no need to 'remove' the empty xmlns attribute. The whole reason that the empty xmlns attrib is added is because the namespace of your childnodes is empty (= '') and therefore differ from your root node. Adding the same namespace to your childs as well will solve this 'side-effect'.
XNamespace xmlns = XNamespace.Get("http://my.namespace");
// wrong
var doc = new XElement(xmlns + "Root", new XElement("Firstelement"));
// gives:
<Root xmlns="http://my.namespace">
<Firstelement xmlns="" />
</Root>
// right
var doc = new XElement(xmlns + "Root", new XElement(xmlns + "Firstelement"));
// gives:
<Root xmlns="http://my.namespace">
<Firstelement />
</Root>
I have an string parameter with xml content in it. Basically the string have an XML inside.
string S = funcThatReturnsXML (parameters);
S have the next text:
<?xml version="1.0" encoding="utf-8" ?>
<tagA>
<tagB>
<tagBB>
..
.
.
</tagBB>
.
.
</tagB>
<tagC>
..
..
.
</tagC>
</tagA>
The funcThatReturnsXML (parameters) creates an XmlDocument object but the return it as a string, I cant change this function, to much stuff works with it.
Tried to create XmlDocument objetc but the SelectSingleNode return null.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
XmlNode root = xmlDoc.SelectSingleNode("tagB");
How can I delete from string S (not XML Object) specific node, for example <tagB>
EDIT: this is the XML I tested with:
<?xml version="1.0" ?>
- <Request xmlns:xsi="http://www.mysite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <info xmlns="http://www.mysite.com">
<RequestTR>54</RequestTR>
<time>2013-12-22</time>
</info>
- <Parameters xmlns="http://www.mysite.com">
<id>3</id>
<name>2</name>
</Parameters>
<title>Request</title>
</Request>
Try this:
string S = funcThatReturnsXML(parameters);
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants("tagB");
nodeToRemove.Remove();
That will remove all nodes named "tagB" from string S which contains xml.
UPDATE 1:
Sorry, i missed to include one more line:
S = doc.ToString();
My first code above removed "tagB" from doc but didnt save it back to S variable.
UPDATE 2:
I tested with following xml which contain attribute:
<tagA attribute="value">
<tagB>
<tagBB>
</tagBB>
</tagB>
<tagC></tagC>
</tagA>
and the output of Console.WriteLine(S):
<tagA attribute="value">
<tagC></tagC>
</tagA>
UPDATE 3:
Given your updated xml format, I know why my previous code didn't work for you. That was because your xml have namespace (xmlns) declared. The solution is to use LocalName when searching for the node to be removed, that will search for node name while ignoring its namespace. The follwoing example shows how to remove all "info" node:
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants().Where(o => o.Name.LocalName == "info");
nodeToRemove.Remove();
S = doc.ToString();
If you can determine the particular outer element to remove from the returned XML, you could use LINQ to XML:
var returnedXml = funcThatReturnsXML(parameters);
var xmlElementToRemove = funcThatReturnsOuterElement(returnedXml);
var xelement = XElement.Load("XmlDoc.txt");
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
For example:
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
// pretend this is the funThatReturnsXML return value
var returnedXml = "<tagB><tagBB></tagBB></tagB>";
// get the outer XML element name
var xmlElementToRemove = GetOuterXmlElement(returnedXml);
// load XML from where ever
var xelement = XElement.Load("XmlDoc.txt");
// remove the outer element and all subsequent elements
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
}
static string GetOuterXmlElement(string xml)
{
var index = xml.IndexOf('>');
return xml.Substring(1, index - 1);
}
}
Note that the above is a "greedy" removal method, if there is more than once element with the name returned via the GetOuterXmlElemet method they will all be removed. If you want a specific instance to be removed then you will require something more sophisticated.
Building on your edit:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
var nodeA = xmlDoc.SelectSingleNode("/tagA");
var nodeB = nodeA.SelectSingleNode("tagB");
nodeA.RemoveChild(nodeB);
To remove (possibly) multiple tagB nodes in unknown positions, you may try:
var bees = xmlDoc.SelectNodes("//tagB");
foreach (XmlNode bee in bees) {
var parent = bee.ParentNode;
parent.RemoveChild(bee);
}