tags:

views:

33

answers:

1

I want to use XPath to filter information from an XML.

XPathFactory factory = XPathFactory.newInstance();
     XPath xpath = factory.newXPath();
     Object result = null;
     // set the custom resolver to compares dates
     xpath.setXPathFunctionResolver(new DateValidatorContext(PubUtils.getInstance().parseDate(date), operator));
     try {    
      PubUtils.getInstance().printDOM(feed, System.out);
      XPathExpression expr = xpath.compile("//entry/content/artifact/resourceUri/text()");
   result = expr.evaluate(feed, XPathConstants.NODESET);
  } catch (XPathExpressionException e) {
   PublishService.logger.debug(e.getMessage());
   return null;
  }
     NodeList nodes = (NodeList) result;
     for (int i = 0; i < nodes.getLength(); i++) {
         System.out.println(nodes.item(i).getNodeValue()); 
     }

I implemented the XPathResolver that checks if the date is (EQUALS/GREATER/LOWER) than an given date.

import java.util.Date;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionException;
import javax.xml.xpath.XPathFunctionResolver;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import ....Operator;

/**
 * XPathFunctionResolver to compare dates
 * 
 */
public class DateValidatorContext implements XPathFunctionResolver {

 private static final QName name = new QName(null, "compare-date");
 private Date referenceDate = null; 
 Operator operator = Operator.EQUALS;

 /**
  * Sets the variables for comparison. Default comparison is equals
  * 
  * @param referenceDate
  *            date to compare to
  */
 public DateValidatorContext (Date referenceDate){
  this.referenceDate = referenceDate;
 }

 /**
  * Sets the variables for comparison
  * 
  * @param referenceDate
  *            date to compare to
  * @param operator
  *            type of operation to execute (EQUALS, GREATER, LOWER)
  */
 public DateValidatorContext (Date referenceDate, Operator operator){
  this.referenceDate = referenceDate;
  this.operator = operator;
 }

 public XPathFunction resolveFunction(QName name, int arity) {
  if (name.equals(DateValidatorContext.name) && arity == 1) {
   return new DateValidator(referenceDate, operator);
  }
  return null;
 }

 /**
  * XPathFunction to compare dates
  * 
  */
 public class DateValidator implements XPathFunction{

  private Date referenceDate = null; 
  Operator operator = Operator.EQUALS;

  /**
   * Set the values for comparison
   * 
   * @param referenceDate
   *            date to compare to
   * @param operator
   *            type of operation to execute (EQUALS, GREATER, LOWER)
   */
  public DateValidator (Date referenceDate, Operator operator){
   this.referenceDate = referenceDate;
   this.operator = operator;
  }

  public Object evaluate(List args) throws XPathFunctionException {
   if(args.size()!= 1){
    throw new XPathFunctionException("Wrong number of arguments to compare-date()");
   }

   Date feedItemDate;
      Object o = args.get(0);

      // perform conversions
      if (o instanceof String) feedItemDate = PubUtils.getInstance().parseDate((String) args.get(0));
      else if (o instanceof Boolean) feedItemDate = null;
      else if (o instanceof Double) feedItemDate = null;
      else if (o instanceof NodeList) {
          NodeList list = (NodeList) o;
          Node node = list.item(0);
          feedItemDate= PubUtils.getInstance().parseDate(node.getTextContent());
      }
      else {
          throw new XPathFunctionException("Could not convert argument type");
      }

      if(referenceDate != null && feedItemDate != null){
       int dateCompare = feedItemDate.compareTo(referenceDate);
       switch(operator){
        case EQUALS: 
         if(dateCompare == 0){
          return Boolean.TRUE;
         }         
         break;
        case GREATER:
         if(dateCompare > 0){
          return Boolean.TRUE;
         }         
         break;
        case LOWER:
         if(dateCompare < 0){
          return Boolean.TRUE;
         }         
         break;
        default: 
         break;
       }
      }

   return Boolean.FALSE;
  }
 }
}

The problem is that with the current expression

"//entry/content/artifact/resourceUri/text()"

I'm getting all the content from the XML. How can I fix this to get only the information I need from the XML?

Sample XML:

<?xml version="1.0" encoding="UTF-8"?>
<recentArtifactsFeed>
 <entry>
  <updated>2010-07-08T00:54:22.859Z</updated>
  <content lang="english" type="application/xml">
    <artifact><resourceUri>../types/_WDKRIYorEd-hG9AeAoU8_g</resourceUri></artifact>
  </content>
 </entry>
</recentArtifactsFeed>
A: 

i just tested your xpathExpression ("//entry/content/artifact/resourceUri/text()"), without the XPathFunctionResolver, this gives the expected result.

I cannot test with the XPathFunctionResolver, because the code is not complete (Operator, PubUtils, ...), but i checked the javadoc:

In particular, the resolver is only called for functions in an another namespace (functions with an explicit prefix). This means that you cannot use the XPathFunctionResolver to implement specifications like XML-Signature Syntax and Processing which extend the function library of XPath 1.0 in the same namespace. This is a consequence of the design of the resolver.

Following gives also a good start: http://xml.apache.org/xalan-j/xpath_apis.html#functionresolver

In short, it takes three steps to use functionresolvers:

  1. create XPathFunction
  2. create XPathFunctionResolver
  3. create NamespaceContext
davyM
It only works for namespaces ... too bad, I had to do a manual implementation to validate the dates.Thanks for the response :)
Gary