1

I have looked and tried solutions given, but I can't get any to work for my xpath. Using Java 7 through Tomcat7 (through a JSP page) Here's my sample XML:

<table name='phonebook'>
<record>
    <field name='fname'>John</field>
    <field name='lname'>Dee</field>
    <field name='City'>London</field>
    <field name='Phone'>020123</field>
</record>
<record>
    <field name='fname'>JOHN</field>
    <field name='lname'>Smith</field>
    <field name='City'>london</field>
    <field name='Phone'>020456</field>
</record>
<record>
    <field name='fname'>Marble</field>
    <field name='lname'>SMith</field>
    <field name='City'>Bristol</field>
    <field name='Phone'>0117123</field>
</record>
</table>
</database>

I have a search (which works fine) but it is case sensitive. Note that I spelled John, London and Smith with different cases, and I want those to match when I search. So how to find them with a case in-senstive search? Here's my Java/XPATH:

XPathExpression expr = xpath.compile("//table[@name='phonebook']/record[upper-case(field[@name='fname'])='JOHN']");

So here I want to find all records that have an fname of value JOHN regardless of upper/lower case. It gives me a javax.xml.xpath.XPathExpressionException. When I take away the 'upper-case( .... )' it works (but case sensitive). I've also tried 'match()' with no success.

mljm
  • 327
  • 3
  • 13
  • Is `upper-case` a standard function in XPath? I don’t see it listed in [the specification](https://www.w3.org/TR/xpath/#section-String-Functions). – VGR Oct 26 '16 at 18:12
  • Duplicate of [case insensitive xpath contains() possible?](http://stackoverflow.com/questions/8474031/case-insensitive-xpath-contains-possible) (Same case-insensitve techniques work without `contains()`.) – kjhughes Oct 26 '16 at 18:41
  • `upper-case()` requires XPath 2.0 – kjhughes Oct 26 '16 at 18:42

2 Answers2

1

This is from the documentation of javax.xml.xpath :

Package javax.xml.xpath Description

This package provides an object-model neutral API for the evaluation of XPath expressions and access to the evaluation environment.

The following XML standards apply:

XML Path Language (XPath) Version 1.0

The upper-case function is not a part of that XPath specification.

However you can use a workaround as shown below :

Object result = (Object) xpath.evaluate("//table[@name='phonebook']/record[field[@name='fname']]", xml, XPathConstants.NODESET);
if ( result != null && result instanceof NodeList )
{
    NodeList nodeList = (NodeList)result;
    List<Node> filteredList = new ArrayList<Node>();
    if ( nodeList.getLength() > 0 )
    {
        for ( int i = 0; i < nodeList.getLength(); i++ )
        {
            Node recordNode = nodeList.item( i );
            NodeList list = recordNode.getChildNodes();
            for ( int j = 0 ; j < list.getLength(); j++ )
            {
                 Node fName = list.item(j);
                 if ( fName.getNodeType() == Element.ELEMENT_NODE )
                 {
                      Element fNameElem = (Element)fName;

                      String nameAttr = fNameElem.getAttribute( "name" );
                      if ( nameAttr != null && nameAttr.equals( "fname" ) && fNameElem.getTextContent() != null && fNameElem.getTextContent().equalsIgnoreCase("JOHN") )
                      {
                         filteredList.add( recordNode );
                         break;
                      }
                 }
             }
        }
    }
}

Use xpath to look for record elements which have a field with attribute name='fname' and iterate through those elements and remove those which don't have text content as JOHN ignoring the case

SomeDude
  • 13,876
  • 5
  • 21
  • 44
  • In the JSP page I'm loading 'javax.xml.xpath.*' Is that not XPATH v2, and if not, is it possible to load xpath v2? – mljm Oct 26 '16 at 19:33
  • @mjlm, javax.xml.xpath is based on xpath 1.0. If you want to leverage xpath 2.0 a third party library like saxonica might help. You may want to take a look at http://stackoverflow.com/questions/6624149/xpath-2-0-for-java-possible – SomeDude Oct 27 '16 at 13:14
  • The workaround works, thanks! Another part of my code expected a NodeList, so I tried to get the results in a new NodeList, but found that is not as straightforward as it seems. So ended up changing the other parts to accept a List. Still seems a bit of a miss not to have Upper/lower case in core XPath, but at least it explains why the XPath-tester did not give an error, as that one uses XPath2 – mljm Oct 28 '16 at 12:53
  • Glad it helped. Upvote/accept the answer if you could. Thanks! – SomeDude Oct 28 '16 at 12:55
  • PS: .It is also still pretty fast considering I have almost 10,000 records each with 28 fields in my XML database. – mljm Oct 28 '16 at 13:02
0

There is no upper-case function in XPath 1.0. However, Java’s XPath API lets you define your own functions:

XPathFunction upperCase = new XPathFunction() {
    @Override
    @SuppressWarnings("rawtypes")
    public Object evaluate(List args) {
        StringBuilder text = new StringBuilder();

        for (Object arg : args) {
            NodeList list = (NodeList) arg;

            int len = list.getLength();
            for (int i = 0; i < len; i++) {
                Node node = list.item(i);
                String s = node.getTextContent();
                if (s != null) {
                    text.append(s.toUpperCase());
                }
            }
        }

        return text.toString();
    }
};

xpath.setXPathFunctionResolver((f, count) ->  {
    return "upper-case".equals(f.getLocalPart()) ? upperCase : null;
});

XPathExpression expr = xpath.compile("//table[@name='phonebook']/record[dummy:upper-case(field[@name='fname'])='JOHN']");

Notice that I added a dummy namespace prefix to upper-case in the XPath expression. Functions with no namespace will not be resolved with the XPathFunctionResolver.

VGR
  • 40,506
  • 4
  • 48
  • 63