trying to delete xmlns namespaces of nodes - c#

I've got some problems with xml messages and c#.
The problem is a root element with no namespaces and all the namespaces are in the nodes.
I've got a part of the script running to delete the namespaces so I can read all the xml messages that will be sent to the webserver.
The message that gives the problems:
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetOrderResponseRequest xmlns="http://www.edibulb.nl/XML/Order:2">
<Header>
<UserName xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2">FBT_000390</UserName>
<Password xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2">1FWcgwrx9</Password>
<MessageID xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" schemeDataURI="8719604082016">8719604082016100376</MessageID>
<MessageDateTime xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" format="304">20170523090413+02:00</MessageDateTime>
</Header>
<Body>
<AgentParty>
<PrimaryID xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" schemeID="251" schemeAgencyName="EBC">8719604178115</PrimaryID>
</AgentParty>
<GetOrderResponseDetails>
<MutationDateTime xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" format="304">20170510000000+02:00</MutationDateTime>
<BuyerParty xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2">
<PrimaryID xmlns="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:3" schemeID="251" schemeAgencyName="EBC">8719604082016</PrimaryID>
</BuyerParty>
</GetOrderResponseDetails>
</Body>
</GetOrderResponseRequest>
</soap:Body>
</soap:Envelope>
And here is the part of the script to translate on the webservice
the code below works perfectly fine if there are prefixes involved.
but it wont work with the xml defined above.
Here is the class that I call from the webservice.
First I check in the xml string if there are any prefixes.
RemoveNamespace remove = new RemoveNamespace();
public string orderrequest(string xmldoc, string ivbglns, bool success)
{
if (success == true)
{
if (xmldoc.Contains(":UserName"))
{
string xdoc = remove.removeall(xmldoc);
docx = new XmlDocument();
docx.LoadXml(xdoc);
}
else if(xmldoc.Contains("<UserName xmlns"))
{
string xdoc = remove.removexlmns(xmldoc);
docx = new XmlDocument();
docx.LoadXml(xmldoc);
}
// rest of the code for the response
}
}
and below the RemoveNameSpace part:
public string removeall(string xdoc)
{
string docx = RemoveAllNamespaces(xdoc);
return docx;
}
public static string RemoveAllNamespaces(string xmldoc)
{
XElement documentwithoutns = XRemoveAllNamespaces(XElement.Parse(xmldoc));
return documentwithoutns.ToString();
}
private static XElement XRemoveAllNamespaces(XElement Xmldoc)
{
if (!Xmldoc.HasElements)
{
XElement element = new XElement(Xmldoc.Name.LocalName);
element.Value = Xmldoc.Value;
foreach (XAttribute attribute in Xmldoc.Attributes())
element.Add(attribute);
return element;
}
return new XElement(Xmldoc.Name.LocalName, Xmldoc.Elements().Select(el => XRemoveAllNamespaces(el)));
}
public string removexlmns(string xdoc)
{
string pattern = "\\s+xmlns\\s*(:\\w)?\\s*=\\s*\\\"(?<url>[^\\\"]*)\\\"";
MatchCollection matchcol = Regex.Matches(xdoc, pattern);
foreach (Match m in matchcol)
{
xdoc = xdoc.Replace(m.ToString(), "");
}
return xdoc;
}
The error it returns is: The Prefix "cannot be redefined from" to 'urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2' within the same start element tag.
I'm in search for a solution for this. The xml from above is a message thats beyond my control.
with Kind regards
Stephan

I would very strongly suggest you use the namespaces in whatever XML processing you are doing after this. Stop trying to remove them!
If you must remove them, it's worth noting that XElement.Name is mutable. You can remove all the namespace declarations and set all the names to their local names.
var doc = XDocument.Parse(xml);
doc.Descendants()
.Attributes()
.Where(x => x.IsNamespaceDeclaration)
.Remove();
foreach (var element in doc.Descendants())
{
element.Name = element.Name.LocalName;
}
See this fiddle for a demo.

Related

Selecting inner text of XML nodes and adding to list

I have the following XML file called file.xml:
<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config">
<defaults>
<serializer>pof</serializer>
</defaults>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>broadcast-data|position</cache-name>
<scheme-name>broadcast</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>broadcast-data|position-audit</cache-name>
<scheme-name>broadcast-remote</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>broadcast-data|trade</cache-name>
<scheme-name>broadcast-remote</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
</cache-config>
I'm trying to get the inner text of all the cache names, which exist under each cache-mapping node, and put them all in a list. I have this Model.cs class to do that.
class Model
{
private XmlDocument cacheFile = new XmlDocument();
private List<string> cacheNames = new List<string>();
private int nameCount = 0;
public Model()
{
this.loadNames();
}
public void loadNames()
{
try //exception handling
{
cacheFile.Load("../../resources/file.xml");
}
catch (System.IO.FileNotFoundException)
{
Debug.WriteLine("File not found!");
Environment.Exit(1);
}
catch (System.ArgumentException)
{
Debug.WriteLine("Invalid path!");
Environment.Exit(1);
}
catch (Exception e)
{
Debug.WriteLine("Exception thrown!");
Debug.WriteLine(e);
Environment.Exit(1);
}
//get cache names
XmlNodeList nodes = cacheFile.SelectNodes("/cache-config/caching-scheme-mapping/cache-mapping");
foreach (XmlNode node in nodes)
{
string name = node.FirstChild.InnerText;
cacheNames.Add(name);
nameCount++;
}
}
//accessors
public List<string> getCacheNames()
{
return cacheNames;
}
public int getNameCount()
{
return nameCount;
}
}
However, every time I create a Model object and then check if the List was loaded up, it tells me the list is empty! It appears as though the foreach loop never actually runs, or as if the program can't find the nodes I'm specifying. Please help.
If you use LINQ to XML, this is really quite simple:
XNamespace ns = "http://xmlns.oracle.com/coherence/coherence-cache-config";
var doc = XDocument.Load("../../resources/file.xml");
cacheNames = doc.Descendants(ns + "cache-name").Select(e => e.Value).ToList();
You don't need to keep a separate count of items, you can get this from the list:
cacheNames.Count;
As an aside, idiomatic C# uses pascal casing for methods and properties, so if you stuck to this your methods would start with a capital letter - e.g. GetCacheNames.
This has been puzzling many and has been asked many times here in SO. Your XML has default namespace here :
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
Descendant elements inherit ancestor default namespace, unless otherwise specified (using explicit namespace prefix or having local default namespace that point to different namespace URI). Using XmlDocument you can use XmlNamespaceManager to register prefix to namespace URI mapping, and use the registered prefix properly in your XPath query, for example :
var nsMgr = new XmlNamespaceManager(new NameTable());
nsMgr.AddNamespace("d", "http://xmlns.oracle.com/coherence/coherence-cache-config");
var xpath = "/d:cache-config/d:caching-scheme-mapping/d:cache-mapping";
XmlNodeList nodes = cacheFile.SelectNodes(xpath, nsMgr);
Anyway, if you have just started this, switching to newer class XDocument would be a better option.

Using xpath select how do I get the value of this element in example?

with this XML
<?xml version="1.0" encoding="UTF-8"?>
<createTransactionResponse xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<refId>999999999</refId>
<messages>
<resultCode>Ok</resultCode>
<message>
<code>I00001</code>
<text>Successful.</text>
</message>
</messages>
<transactionResponse>
<responseCode>1</responseCode>
<authCode>HH1D69</authCode>
<avsResultCode>Y</avsResultCode>
<cvvResultCode>P</cvvResultCode>
<cavvResultCode>2</cavvResultCode>
<transId>2228993425</transId>
<refTransID />
<transHash>916EE7527B17B62F62AA72B4C71F8322</transHash>
<testRequest>0</testRequest>
<accountNumber>XXXX0015</accountNumber>
<accountType>MasterCard</accountType>
<messages>
<message>
<code>1</code>
<description>This transaction has been approved.</description>
</message>
</messages>
<userFields>
<userField>
<name>MerchantDefinedFieldName1</name>
<value>MerchantDefinedFieldValue1</value>
</userField>
<userField>
<name>favorite_color</name>
<value>blue</value>
</userField>
</userFields>
</transactionResponse>
</createTransactionResponse>
I would like to get "resultCode", from here
<messages><resultCode>Ok</resultCode><message>
But the xpath I'm using does not give me the value of resultCode Ok.
What am I doing wrong?
--
static void Main(string[] args)
{
string myXML = #"<?xml version=""1.0"" encoding=""utf-8""?><createTransactionResponse xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""AnetApi/xml/v1/schema/AnetApiSchema.xsd""><refId>999999999</refId><messages><resultCode>Ok</resultCode><message><code>I00001</code><text>Successful.</text></message></messages><transactionResponse><responseCode>1</responseCode><authCode>HH1D69</authCode><avsResultCode>Y</avsResultCode><cvvResultCode>P</cvvResultCode><cavvResultCode>2</cavvResultCode><transId>2228993425</transId><refTransID /><transHash>916EE7527B17B62F62AA72B4C71F8322</transHash><testRequest>0</testRequest><accountNumber>XXXX0015</accountNumber><accountType>MasterCard</accountType><messages><message><code>1</code><description>This transaction has been approved.</description></message></messages><userFields><userField><name>MerchantDefinedFieldName1</name><value>MerchantDefinedFieldValue1</value></userField><userField><name>favorite_color</name><value>blue</value></userField></userFields></transactionResponse></createTransactionResponse>";
string myValue = XMLSelect(myXML, "createTransactionResponse/messages/message/resultCode");
//myValue should = "Ok" but it does not :(
}
public static string XMLSelect(string _xmldoc, string _xpath)
{
string returnedValue = string.Empty;
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(_xmldoc);
XmlElement root = doc.DocumentElement;
returnedValue = (string)doc.SelectNodes(_xpath)[0].InnerText;
}
catch (Exception ex)
{
return "";
}
return returnedValue;
}
Your first problem is that the XML you originally gave isn't valid. You're masking that by returning "" whenever you encounter an exception, so you don't have any information any more.
So the first thing to do IMO is remove the spurious exception handling:
public static string XMLSelect(string _xmldoc, string _xpath)
{
string returnedValue = string.Empty;
XmlDocument doc = new XmlDocument();
doc.LoadXml(_xmldoc);
XmlElement root = doc.DocumentElement;
return (string)doc.SelectNodes(_xpath)[0].InnerText;
}
Now, the problem with the XPath is that all the elements in your XML are in a namespace of AnetApi/xml/v1/schema/AnetApiSchema.xsd - so you'll probably want an XmlNamespaceManager. It's slightly tricky to separate that properly given the way that you've split the load from the XPath, but for the moment you could just introduce an alias of ns for the right namespace.
Next, your XPath is incorrect in that it looks for message/resultCode, when those two elements are peers. You don't want the message part.
Here's a short but complete program which fixes all those problems:
using System;
using System.Xml;
public class Program
{
static void Main(string[] args)
{
// As per the question
string myXML = "...";
string myValue = XMLSelect(myXML, "ns:createTransactionResponse/ns:messages/ns:resultCode");
Console.WriteLine(myValue);
}
public static string XMLSelect(string _xmldoc, string _xpath)
{
string returnedValue = string.Empty;
XmlDocument doc = new XmlDocument();
doc.LoadXml(_xmldoc);
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("ns", "AnetApi/xml/v1/schema/AnetApiSchema.xsd");
XmlElement root = doc.DocumentElement;
return (string)doc.SelectNodes(_xpath, nsm)[0].InnerText;
}
}
If you could use LINQ to XML instead, and simply work on the document instead of passing in the XML text and an XPath selector, it would be simpler:
XDocument doc = XDocument.Parse(xml);
XNamespace ns = "AnetApi/xml/v1/schema/AnetApiSchema.xsd";
string code = (string) doc.Root
.Element(ns + "messages")
.Element(ns + "resultCode");
Console.WriteLine(code);

Reading an XML document with XMLDocument C# 'password' string not returning

I'm trying to read the text in between <keyMaterial></keyMaterial>
I tried using //WLANProfile/MSM/security/sharedKey as the element route, seen in the code below. It refuses to return a value. I have run through the debugger and after the breakpoint at the line: XmlNodeList sharedKeyNodes = wifiProfile.SelectNodes("//WLANProfile/MSM/security/sharedKey");
the SharedKeyNodes object doesn't return a count for. I know it's just a matter of figuring out the element route so I'm not coming here completely hopeless...
System.Xml.XPathNodeList
My XML looks like this:
<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>nosignal</name>
<SSIDConfig>
<SSID>
<hex>6E6F7369676E616C</hex>
<name>nosignal</name>
</SSID>
<nonBroadcast>true</nonBroadcast>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<autoSwitch>false</autoSwitch>
<MSM>
<security>
<authEncryption>
<authentication>WPA2PSK</authentication>
<encryption>AES</encryption>
<useOneX>false</useOneX>
<FIPSMode xmlns="http://www.microsoft.com/networking/WLAN/profile/v2">false</FIPSMode>
</authEncryption>
<sharedKey>
<keyType>passPhrase</keyType>
<protected>true</protected>
<keyMaterial>01000000D</keyMaterial>
</sharedKey>
</security>
</MSM>
</WLANProfile>
[ EDIT with the help of LB the new code looks like this and it WORKS! ]
[ For anyone that is struggling with a similar problem. ]
My Class is:
class ProfileManager
{
public static string readProfile() {
XmlDocument wifiProfile = new XmlDocument();
string path = #"C:\temp\nosignal.xml";
string password = "";
wifiProfile.Load(path);
XmlNamespaceManager mgr = new XmlNamespaceManager(wifiProfile.NameTable);
mgr.AddNamespace("ns", "http://www.microsoft.com/networking/WLAN/profile/v1");
XmlNodeList sharedKeyNodes = wifiProfile.SelectNodes("//ns:WLANProfile/ns:MSM/ns:security/ns:sharedKey", mgr);
foreach (XmlNode itemNode in sharedKeyNodes)
{
XmlNode keyMaterialNode = itemNode.SelectSingleNode("ns:keyMaterial", mgr);
if (keyMaterialNode != null)
{
password = keyMaterialNode.InnerText;
}
}
return password;
}
}
I'm close, but still just a bit stuck. Any help would be appreciated!!! Thank you!
You don't use the default XmlNamespace "http://www.microsoft.com/networking/WLAN/profile/v1"
wifiProfile.Load(path);
XmlNamespaceManager mgr = new XmlNamespaceManager(wifiProfile.NameTable);
mgr.AddNamespace("ns", "http://www.microsoft.com/networking/WLAN/profile/v1");
XmlNodeList sharedKeyNodes = wifiProfile.SelectNodes("//ns:WLANProfile/ns:MSM/ns:security/ns:sharedKey",mgr);

How to edit an Xml file's element values

I have this Xml file, and I want to edit any of the elements like homepage or search_provider.
<?xml version="1.0" encoding="utf-8"?>
<Preferences>
<personal>
<homepage>http://duckduckgo.com</homepage>
<search_provider>DuckDuckGo</search_provider>
<search_provider_url>http://duckduckgo.com/?q=</search_provider_url>
</personal>
</Preferences>
The following is the C# code I'm using to attempt to change the homepage element. Let's say I run saveSetting("homepage", "http://google.com");
public static void saveSetting(String settingName, String newvalue)
{
XmlDocument xml = new XmlDocument();
xml.Load(userSettingsFile);
foreach (XmlElement element in xml.SelectNodes("Preferences"))
{
foreach (XmlElement oldsettingname in element)
{
element.SelectSingleNode(settingName);
XmlNode settingtosave = xml.CreateElement(settingName);
settingtosave.InnerText = newvalue;
element.ReplaceChild(settingtosave, oldsettingname);
xml.Save(userSettingsFile);
}
}
}
Now, while this works to an extent and does change the specified value, it also deletes the entire personal element.
<?xml version="1.0" encoding="utf-8"?>
<Preferences>
<homepage>http://google.com</homepage>
</Preferences>
Hopefully someone can help me out! I've been searching for the last two days for a solution and this is the closest I've come to getting the code to work the way I need it to.
You can just use LINQ to XML like this:
public static void saveSetting(String settingName, String newvalue)
{
var xmlDocument = XDocument.Load("path");
var element = xmlDocument.Descendants(settingName).FirstOrDefault();
if (element != null) element.Value = newvalue;
xmlDocument.Save("path");
}
See this documentation for more details: Modifying Elements, Attributes, and Nodes in an XML Tree
keep your current code and just modify a few lines:
public static void saveSetting(String settingName, String newvalue)
{
XmlDocument xml = new XmlDocument();
xml.Load(userSettingsFile);
foreach (XmlElement element in xml.SelectNodes("Preferences"))
{
if(element.Name.Equals(settingName))
{
element.InnerText = newvalue;
break;
}
}
xml.Save(userSettingsFile);
}

XPath not working as expected

I have an XML document in this format:
<?xml version="1.0" encoding="utf-8" ?>
<SupportedServices>
<Service>
<name>Google Weather</name>
<active>Yes</active>
</Service>
...
</SupportedServices>
And I'm trying to parse the XML file like so:
public void InitializeDropDown(string XmlFile, string xpath)
{
XmlDocument doc = new XmlDocument();
doc.Load(XmlFile);
var rootNode = doc.DocumentElement;
var serviceList = rootNode.SelectNodes(xpath);
Parallel.ForEach(serviceList.Cast<XmlNode>(), service =>
{
if (Properties.Settings.Default.ServiceActive &&
Properties.Settings.Default.ServiceName == service.InnerText)
{
WeatherServicesCBO.Items.Add(service.InnerText);
}
});
}
The issue I'm having is both values (name & active) are selected so it would look like Google WeatherYes, when All I'm wanting is Google Weather. Can someone tell me what's wrong with my XPath (which is here):
InitializeDropDown("SupportedWeatherServices.xml", "descendant::Service[name]");
The XPath should be //Service/name
var serviceList = rootNode.SelectNodes("//Service/name");
or descendant::Service/name, if you like this syntax more.

Categories

Resources