ID ID ID

Translate XPath to JXPath

1k Views Asked by At

I've got the following XML:

 <?xml version="1.0" encoding="UTF-8"?>
        <CreateReservation>
           <NewReservation>
              <Profile op="D">
                 <ProfileID>ID</ProfileID>
                 <ProfileType>TYPE</ProfileType>
              </Profile>
              <Number>123456</Number>
           </NewReservation>

marshalled with JAXB from CreateReservation class in the following way:

CreateReservation request = new CreateReservation("123456", "D");
String xpathExpr = "boolean(//*[local-name()='CreateReservationRQ']//@op='D')"
JAXBContext context = JAXBContext.newInstance(CreateReservation.class);
marshaller = context.createMarshaller();    
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
marshaller.marshal(request, document); //line creates xml presented above

//EVALUATION
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.evaluate(xpathExpr, document, XPathConstants.BOOLEAN); //line evaluates xpath and returns true

Summing everythig up, presented code marshalls CreateReservation class, create corresponding XML out of it, and with xpath expression checks if created xml has any <CreateReservation> node, which has any child with op="D" attribute. The check is done with the following xpath expression:

boolean(//*[local-name()='CreateReservationRQ']//@op='D')

The condition works fine, fot presented code sample. Right now I would like to get rid of unnecessary marshalling of CreateReservation class. To do so, I would like to switch from JAXB to JXPath:

PathContext jXPathContext = JXPathContext.newContext(obj);
Object obj = jXPathContext.getValue(CONDITION);
if (obj != null) {
   //code should behave exactly the same, as my previous model
}

obj- member of CreateReservation class (it's structure is exactly the same as presented in xml above)

CONDITION- my JXPath query

The idea is, that line:

  Object obj = jXPathContext.getValue(CONDITION);

Returns any node, when CreateREservation class has op field equal to 'D'. If no node is present null is returned. Functionally code should behave exactly the same as previously. Could you tell me how my CONDITION should look like?

1

There are 1 best solutions below

0
zakaiter On

For dynamic xpath to object implementation you can have a look into this.

Example 1: JavaBean Property Access

JXPath can be used to access properties of a JavaBean.

 public class Employee {
    public String getFirstName(){
       ...
    }
 }

 Employee emp = new Employee();
 JXPathContext context = JXPathContext.newContext(emp);
 String fName = (String)context.getValue("firstName");

In this example, we are using JXPath to access a property of the emp bean. In this simple case the invocation of JXPath is equivalent to invocation of getFirstName() on the bean.

Example 2: Nested Bean Property Access

JXPath can traverse object graphs:

 public class Employee {
    public Address getHomeAddress(){
       ...
    }
 }
 public class Address {
    public String getStreetNumber(){
       ...
    }
 }

 Employee emp = new Employee();
 ...

 JXPathContext context = JXPathContext.newContext(emp);
 String sNumber = (String)context.getValue("homeAddress/streetNumber");

In this case XPath is used to access a property of a nested bean. A property identified by the xpath does not have to be a "leaf" property. For instance, we can extract the whole Address object in above example:

Address addr = (Address)context.getValue("homeAddress");

Example 3: Setting Properties

JXPath can be used to modify property values.

 public class Employee {
    public Address getAddress() {
       ...
    }

    public void setAddress(Address address) {
       ...
    }
 }

 Employee emp = new Employee();
 Address addr = new Address();
 ...

 JXPathContext context = JXPathContext.newContext(emp);
 context.setValue("address", addr);
 context.setValue("address/zipCode", "90190");

Example 4: Creating objects JXPath can be used to create new objects. First, create a subclass of AbstractFactory and install it on the JXPathContext. Then call createPathAndSetValue() instead of "setValue". JXPathContext will invoke your AbstractFactory when it discovers that an intermediate node of the path is null. It will not override existing nodes.

public class AddressFactory extends AbstractFactory {
    public boolean createObject(JXPathContext context,
               Pointer pointer, Object parent, String name, int index){
     if ((parent instanceof Employee) && name.equals("address"){
       ((Employee)parent).setAddress(new Address());
       return true;
     }
     return false;
   }
 }

 JXPathContext context = JXPathContext.newContext(emp);
 context.setFactory(new AddressFactory());
 context.createPathAndSetValue("address/zipCode", "90190");

For detail help look into this link: JxPath Tutorial