Commons JXPath
  1. Commons JXPath
  2. JXPATH-57

evaluate xpath expressions in functions

    Details

    • Type: Improvement Improvement
    • Status: Resolved
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 1.1 Final
    • Fix Version/s: None
    • Labels:
      None
    • Environment:

      Operating System: Windows XP
      Platform: PC

      Description

      It is not clear in the doc comments how to call a function in such a way that
      certain parameters are evaluated as XPath expressions before being passed to
      the method.

      For example, in the PackageFunctions doc comments, it gives the example:

      "util:substring('foo', 1, 2)" is equivalent to "foo".substring(1, 2)

      which is not an especially useful example, as both are inefficient equivalents
      of the expression "fo".

      A more interesting example involve an expression like:

      "util:substring('person/name/middleName', 1, 1)"

      which could be used to get the middle initial of a person. The problem is how
      to indicate that this should be context.getJXPathContext().getValue
      ("person/name/middleName").substring(1, 1) instead
      of "person/name/middleName".substring(1, 1).

      Without the facility to indicate that some parameters are XPath expressions,
      existing methods cannot be used without writing wrapper methods to evaluate
      the XPath expressions.

      If such a facility already exists, the doc comments of PackageFunctions, etc.
      should be updated to describe it.

        Activity

        Hide
        Dmitri Plotnikov added a comment -

        Oh, yeah. That makes sense. The reason the behavior of JXPath has changed is
        this: a few people requested a way to disable the default PackageFunctions,
        because they presented a security problem. In some uses of JXPath, the XPath
        expressions are coming over an internet connection and therefore can be
        maliciously altered by a hacker. For example, the hacker may send something
        like "java.lang.System.exit(1)". For this reason, folks wanted more control
        over which functions could be called from an expression.

        Show
        Dmitri Plotnikov added a comment - Oh, yeah. That makes sense. The reason the behavior of JXPath has changed is this: a few people requested a way to disable the default PackageFunctions, because they presented a security problem. In some uses of JXPath, the XPath expressions are coming over an internet connection and therefore can be maliciously altered by a hacker. For example, the hacker may send something like "java.lang.System.exit(1)". For this reason, folks wanted more control over which functions could be called from an expression.
        Hide
        Xavier Dury added a comment -

        As I'm still having the same problem with 1.2, I've been digging a little
        deeper: it seems that using setFunctions() on a context doesn't allow me to
        use regular functions anymore.

        I'm using the following custom implementation of JXPathContextFactory (which
        worked fine with 1.1):

        /**

        • JXPathContextFactory custom implementation.
        • @author xd
          */
          public class JXPathContextFactoryImpl extends
          JXPathContextFactoryReferenceImpl {

        /** The logger. */
        private static Log log = LogFactory.getLog(JXPathContextFactoryImpl.class);

        /** The function library. */
        private static FunctionLibrary library = new FunctionLibrary();

        static

        { log.trace("Initializing JXPath's TypeUtils."); TypeUtils.setTypeConverter(DefaultTypeConverter.getInstance()); log.trace("Initializing JXPath's DefaultFucntions."); library.addFunctions(new ClassFunctions (DefaultFunctions.class, "default")); }

        /** Default constructor. */
        public JXPathContextFactoryImpl()

        { log.trace("Instantiating custom JXPathContextFactory..."); }

        /**

        • @see org.apache.commons.jxpath.JXPathContextFactory#newContext
          (org.apache.commons.jxpath.JXPathContext,
        • java.lang.Object)
          */
          public JXPathContext newContext(JXPathContext inParentContext, Object
          inContextBean) throws JXPathContextFactoryConfigurationError { JXPathContext context = super.newContext(inParentContext, inContextBean); // The context should return null when accessing a non-existing node // instead of throwing an exception. context.setLenient(true); // Set the factory that will create nodes upon hibernate's metadata. context.setFactory(AbstractFactoryImpl.getInstance()); // Add some default functions. context.setFunctions(library); // THE cause... return context; }

          }

        When I remove the 'context.setFunctions(...)' line, I don't have any problem
        anymore.

            • Break ***

        After searching in CVS logs with a colleague, we found a way to circumvent the
        problem by doing the following:

        library.addFunctions(new PackageFunctions("", null));

        At least, it's working! Thanks for your time, I hope I haven't bothered you
        too much!

        Show
        Xavier Dury added a comment - As I'm still having the same problem with 1.2, I've been digging a little deeper: it seems that using setFunctions() on a context doesn't allow me to use regular functions anymore. I'm using the following custom implementation of JXPathContextFactory (which worked fine with 1.1): /** JXPathContextFactory custom implementation. @author xd */ public class JXPathContextFactoryImpl extends JXPathContextFactoryReferenceImpl { /** The logger. */ private static Log log = LogFactory.getLog(JXPathContextFactoryImpl.class); /** The function library. */ private static FunctionLibrary library = new FunctionLibrary(); static { log.trace("Initializing JXPath's TypeUtils."); TypeUtils.setTypeConverter(DefaultTypeConverter.getInstance()); log.trace("Initializing JXPath's DefaultFucntions."); library.addFunctions(new ClassFunctions (DefaultFunctions.class, "default")); } /** Default constructor. */ public JXPathContextFactoryImpl() { log.trace("Instantiating custom JXPathContextFactory..."); } /** @see org.apache.commons.jxpath.JXPathContextFactory#newContext (org.apache.commons.jxpath.JXPathContext, java.lang.Object) */ public JXPathContext newContext(JXPathContext inParentContext, Object inContextBean) throws JXPathContextFactoryConfigurationError { JXPathContext context = super.newContext(inParentContext, inContextBean); // The context should return null when accessing a non-existing node // instead of throwing an exception. context.setLenient(true); // Set the factory that will create nodes upon hibernate's metadata. context.setFactory(AbstractFactoryImpl.getInstance()); // Add some default functions. context.setFunctions(library); // THE cause... return context; } } When I remove the 'context.setFunctions(...)' line, I don't have any problem anymore. Break *** After searching in CVS logs with a colleague, we found a way to circumvent the problem by doing the following: library.addFunctions(new PackageFunctions("", null)); At least, it's working! Thanks for your time, I hope I haven't bothered you too much!
        Hide
        Dmitri Plotnikov added a comment -

        Try it with JXPath 1.2. I ran your example verbatim, and it worked fine.

        Show
        Dmitri Plotnikov added a comment - Try it with JXPath 1.2. I ran your example verbatim, and it worked fine.
        Hide
        Xavier Dury added a comment -

        still getting 'org.apache.commons.jxpath.JXPathException: Undefined function:
        size' with the above example and last build (2004-08-01).

        Show
        Xavier Dury added a comment - still getting 'org.apache.commons.jxpath.JXPathException: Undefined function: size' with the above example and last build (2004-08-01).
        Hide
        Dmitri Plotnikov added a comment -

        At some point I screwed up the implementation of InitialContext.getValue().
        That's now fixed.

        Show
        Dmitri Plotnikov added a comment - At some point I screwed up the implementation of InitialContext.getValue(). That's now fixed.
        Hide
        Xavier Dury added a comment -

        I noticed some differences between 1.1 and nightly build from 2004-07-29...

        with 1.1, I was able to call regular functions on objects like this:

        JXPathContext context = JXPathContext.newContext(new ArrayList());
        System.out.println(context.getValue("size"));

        which would give 0 (zero) as size.

        now with today's nightly build, I get:

        org.apache.commons.jxpath.JXPathException: Undefined function: size
        at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.getFunction
        (JXPathContextReferenceImpl.java:645)
        at org.apache.commons.jxpath.ri.axes.RootContext.getFunction
        (RootContext.java:118)
        at org.apache.commons.jxpath.ri.compiler.ExtensionFunction.computeValue
        (ExtensionFunction.java:83)
        at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.getValue
        (JXPathContextReferenceImpl.java:314)
        at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.getValue
        ...

        Any chance Kevin was talking about this problem?

        Show
        Xavier Dury added a comment - I noticed some differences between 1.1 and nightly build from 2004-07-29... with 1.1, I was able to call regular functions on objects like this: JXPathContext context = JXPathContext.newContext(new ArrayList()); System.out.println(context.getValue("size ")); which would give 0 (zero) as size. now with today's nightly build, I get: org.apache.commons.jxpath.JXPathException: Undefined function: size at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.getFunction (JXPathContextReferenceImpl.java:645) at org.apache.commons.jxpath.ri.axes.RootContext.getFunction (RootContext.java:118) at org.apache.commons.jxpath.ri.compiler.ExtensionFunction.computeValue (ExtensionFunction.java:83) at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.getValue (JXPathContextReferenceImpl.java:314) at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.getValue ... Any chance Kevin was talking about this problem?
        Hide
        Dmitri Plotnikov added a comment -

        I am still not sure what the problem is.

        1. First of all, the stringToUpperCase function that you quoted looks mighty
        fine to me - I don't see anything wrong with it.

        2. Why do you need to pass the path as a string in the first place? What would
        be wrong with "myextenstionfunctions:stringToUpperCase
        (lastName)" (note the absense of quotes)? It would in fact first
        evaluate 'lastName', then pass the found value to stringToUpperCase.

        Show
        Dmitri Plotnikov added a comment - I am still not sure what the problem is. 1. First of all, the stringToUpperCase function that you quoted looks mighty fine to me - I don't see anything wrong with it. 2. Why do you need to pass the path as a string in the first place? What would be wrong with "myextenstionfunctions:stringToUpperCase (lastName)" (note the absense of quotes)? It would in fact first evaluate 'lastName', then pass the found value to stringToUpperCase.
        Hide
        Kevin Wong added a comment -

        I think you have misunderstood me. I don't want to have to write the method:

        public class MyExtenstionFunctions {
        public static Object stringToUpperCase(ExpressionContext context, String
        xpath)

        { String value = (String)context.getJXPathContext().getValue(xpath); return value.toUpperCase(); }

        }

        and and use an expression like "myextenstionfunctions:stringToUpperCase
        ('lastName')".

        I just want to use the expression "toUpperCase('lastName')" and have it
        evaluate to "SMITH" instead of "LASTNAME".

        The references you listed do not indicate how to do this.

        Show
        Kevin Wong added a comment - I think you have misunderstood me. I don't want to have to write the method: public class MyExtenstionFunctions { public static Object stringToUpperCase(ExpressionContext context, String xpath) { String value = (String)context.getJXPathContext().getValue(xpath); return value.toUpperCase(); } } and and use an expression like "myextenstionfunctions:stringToUpperCase ('lastName')". I just want to use the expression "toUpperCase('lastName')" and have it evaluate to "SMITH" instead of "LASTNAME". The references you listed do not indicate how to do this.
        Show
        Dmitri Plotnikov added a comment - The requested behavior is implemented and documented. See http://jakarta.apache.org/commons/jxpath/users-guide.html#Expression%20Context and http://jakarta.apache.org/commons/jxpath/users-guide.html#Collections%20as% 20Arguments

          People

          • Assignee:
            Unassigned
            Reporter:
            Kevin Wong
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development