Selecting nodes with XPath for binding a subset of classes with JAXB

matt burns picture matt burns · Feb 10, 2012 · Viewed 12.3k times · Source

Simplified Question: What's the XPath to select all XML nodes with an attribute that ends with the string "Notification". The first and third nodes in this snippet:

 <events>
   <event name="CreatedNotification" />
   <event name="InfoLog" />
   <event name="UpdatedNotification" />
 </events>

Detailed Question:

I want to select multiple complexTypes from a xsd schema for binding with JAXB. This works for a single class: OrderStateChangeNotification

<jxb:bindings schemaLocation="apiv2.xsd">
  <jxb:bindings node="//xs:complexType[@name='OrderStateChangeNotification']">
    <inheritance:implements>com.google.checkout.sdk.notifications.Notification</inheritance:implements> 
  </jxb:bindings>
</jxb:bindings>

Here is the relevant snippet from the schema schema file:

  <xs:complexType name="OrderStateChangeNotification">
    <xs:all>
      <xs:element name="new-fulfillment-order-state" type="tns:FulfillmentOrderState" />
      <xs:element name="new-financial-order-state" type="tns:FinancialOrderState" />
      <xs:element name="previous-fulfillment-order-state" type="tns:FulfillmentOrderState" />
      <xs:element name="previous-financial-order-state" type="tns:FinancialOrderState" />
      <xs:element name="reason" type="xs:string" minOccurs="0" />
      <xs:element name="timestamp" type="xs:dateTime" />
      <xs:element name="google-order-number" type="xs:token" />
      <xs:element name="order-summary" type="tns:OrderSummary" minOccurs="0" />
    </xs:all>
    <xs:attribute name="serial-number" type="xs:string" use="required" />
  </xs:complexType>

  <xs:complexType name="ChargeAmountNotification">
    <xs:all>
      <xs:element name="timestamp" type="xs:dateTime" />
      <xs:element name="latest-charge-amount" type="tns:Money" />
      <xs:element name="latest-charge-fee" type="tns:FeeStructure" minOccurs="0" />
      <xs:element name="total-charge-amount" type="tns:Money" />
      <xs:element name="latest-promotion-charge-amount" type="tns:Money" minOccurs="0" />
      <xs:element name="google-order-number" type="xs:token" />
      <xs:element name="order-summary" type="tns:OrderSummary" minOccurs="0" />
    </xs:all>
    <xs:attribute name="serial-number" type="xs:string" use="required" />
  </xs:complexType>

I want the binding to apply to all notification objects. They all end in with "Notification"

I've tried changing the XPath from

//xs:complexType[@name='OrderStateChangeNotification']

to

//xs:complexType[substring(name(), string-length(name()) - 12) = 'Notification']

but it didn't work.

Another approach is to try and select all nodes with the children "order-summary" and "serial-number" as I know only Notification objects have these.

UPDATE: The solution by @Lee Greco correctly selectes the nodes I wanted, but unfortunatly, the inheritance plugin is not compatible with multiple nodes:

[ERROR] XPath evaluation of "//xs:complexType[substring(@name, string-length(@name)-string-length('Notification')+1)='Notification']" results in too many (8) target nodes

I ended up just enumerating them separately.

Answer

boumbh picture boumbh · Sep 3, 2013

I had the same problem. I found out there was a multiple attribute that was used by XJC to allow multiple node match.

I also wanted the binding to apply to every schema locations. xs:anyURI did not work but I found a way to do it using the * token. I added the required="false" attribute in order to ignore schemas that does not contain any match.

<jxb:bindings schemaLocation="*">
  <jxb:bindings node="//xs:complexType[substring(name(), string-length(name()) - 12) = 'Notification']" multiple="true" required="false">
    <inheritance:implements>com.google.checkout.sdk.notifications.Notification</inheritance:implements> 
  </jxb:bindings>
</jxb:bindings>

Edit: I posted this answer without reading the comments of the question. Sorry for that. I'm using maven plugin org.codehaus.mojo:jaxb2-maven-plugin:1.5 with XJC plugin org.jvnet.jaxb2_commons:jaxb2-basics-project:0.6.4 and it seems to work like this...