4

I have a problem similar to the question SelectNodes with XPath ignoring cases but in my case the uppercase/lowercase problem is in the node with the name 'application' (Sometimes is 'Application' other times 'application').

How would i apply the solution of the other post? or a different one applies in this case?

xml:

<?xml version="1.0" encoding="utf-16" ?>
<application>
  <forms>
    <action type="update">
      <form uid="" >
      </form>
    </action>
  </forms>
</application>

In C# 3.5:

XmlNode nodex= oXMLDoc1.SelectSingleNode("Application/forms/action/form/@uid")
nodex.Value="UniqueIDx";//nodex is null :S
Community
  • 1
  • 1
VSP
  • 2,367
  • 8
  • 38
  • 59
  • 1
    Do you have to use XPath? I'd personally use LINQ to XML with a Where clause. Is Application/application always the root node? (If so, can't you just apply the XPath from that?) – Jon Skeet Jun 15 '12 at 10:08
  • 1
    Interesting...could you post an example for this case with LINQ to XML? and one of XPATH with the first node as position instead of name? thanks ^^ – VSP Jun 15 '12 at 10:25
  • @JonSkeet Ups i forgot to reply directly to you, see upper post please – VSP Jun 15 '12 at 10:45

5 Answers5

6

We may convert xml and our variables to lower case.

string value = "aBc";
XmlNode xmlnode = xmldoc.SelectSingleNode(string.Format("/some/path/add[translate(@key, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0}']", value.ToLower()));
mykhailovskyi
  • 680
  • 1
  • 8
  • 19
5

Just use:

*[translate(name(), 'APPLICATION', 'application')='application']
     /forms/action/form/@uid

This selects the wanted attribute correctly in all cases when the current (initial context) node has a child with name, that is any capitalization of the string "application".

XSLT - based verification:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="/">
     <xsl:value-of select=
      "*[translate(name(), 'APPLICATION', 'application')='application']
          /forms/action/form/@uid"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document:

<aPPliCatioN>
 <forms>
   <action>
    <form uid="xyz"/>
   </action>
 </forms>
</aPPliCatioN>

the wanted node is selected and its string value is copied to the output:

xyz

Explanation:

Proper use of the standard XPath functions name() and translate().

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
2

First of all I want to mention that xml is case sensitive, so Application means something else then application, looks to me this should be fixed in the code that generates this xml but if you have no control over that maybe try something like this as your xpath:

"Application/forms/action/form/@uid | application/forms/action/form/@uid"

The | operator will combine the node-sets that are returned from both xpath's in this case it will be one or the other

Gertjan Assies
  • 1,890
  • 13
  • 23
  • Yep knew the xml is case sensitive but effectively the xml generated depended on a third party so this was needed... Your method works fine but i wanted to know if maybe with LINQ to XML or getting the first node by possition would be more optimal as in this solution the code tries to get both nodes... – VSP Jun 15 '12 at 11:03
1

If the root element is the only element where the case of letters can change then you should simply do e.g.

XmlDocument doc = new XmlDocument();
doc.Load("input.xml");
XmlNode nodex= oXMLDoc1.DocumentElement.SelectSingleNode("forms/action/form/@uid");

as already suggested in a comment.

With XDocument you would do e.g.

XDocument doc = XDocument.Load("input.xml");
doc.Root.Element("forms").Element("action").Element("form").SetAttributeValue("uid", "UniqueIDx");

[edit] A comment claims the first example path throws an exception, here is a complete sample that does not throw an exception for me and uses the same path a posted before:

            string xml = @"<application>
  <forms>
    <action type=""update"">
      <form uid="""" >
      </form>
    </action>
  </forms>
</application>";
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            XmlNode nodex = doc.DocumentElement.SelectSingleNode("forms/action/form/@uid");
            nodex.Value = "UniqueIDx";

            doc.Save(Console.Out);
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Trying your first example failed with a nullreferenceexception, worked instead with: */forms/action/form/@uid – VSP Jun 15 '12 at 11:19
  • With the sample you posted I think my suggestion should work, I will edit my post to show a complete example that does not throw an exception for me. – Martin Honnen Jun 15 '12 at 13:09
0

I found the easiest approach was to load the XML file as all lowercase and then just make sure that any subsequent XPath expressions were all lower-case.

Example:

var xmlDoc = new System.Xml.XmlDocument();
var rawFile = System.IO.File.ReadAllText(@"\Path\To\File.xml");
xmlDoc.LoadXml(rawFile.ToLower());

XmlNode node = xmlDoc.SelectSingleNode("//some/path[@attribute1='somevalue' and @attribute2='anothervalue']");
Nico M
  • 173
  • 1
  • 8