Link to home
Start Free TrialLog in
Avatar of ottawamunda
ottawamunda

asked on

XPath and XQuery with XMLBeans

the screen shot of xml view in xml-spy is at:
http://singhsays.blogspot.com/

The problem:
the 'levels' tag should be +/-1 except if its in result.


 now what would be the XPath and Xquery for that?

i plan to validate this using XMLBeans and XQuery. could you please help me out
Thanks a lot


summary of the xml


<rule>
<rule_block>



<selecetion min accurs is =1 and max is unlimited>
<level>.....etc

<qualification min accurs is =0 and max is unlimited>
<level>.....etc

<extension min accurs is =0 and max is unlimited>
<level>.....etc

<resultmin accurs is =1 and max is unlimited>
<level>.....etc


</rule>
</rule_block>




Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Hi ottawamunda,


> The problem:
> the 'levels' tag should be +/-1 except if its in result.

Do you want to extract all the elements that have a level of 1 or -1 except when they are a result
Or do you want an XQuery that makes all these values "+/-1"

I think you should be somewhat more specific about your needs



Cheers!
Avatar of ottawamunda
ottawamunda

ASKER

sorry Gertone i was not clear last time.

i want exact XPath and XQuery that will select row_id for levels  which Dont have values same or +/-1 of previous level, except in the case of result which could be 1 to last level+1.

No row should be selected in case of follwoing xml:


<rule>
<rule_block>
<selecetion>
<level>1</level>
<row_id>1</row_id>
<level>2</level>
<row_id>2</row_id>
</selection>

<qualification >
<level>3</level>
<row_id>3</row_id>
</qualification >

<result >
<level>1</level>
<row_id>4</row_id>
<result>


</rule>
</rule_block>
================problematic xml===========

<rule>
<rule_block>
<selecetion>
<level>1</level>
<row_id>1</row_id>
<level>6</level>                   <-------------------------- only valid values are 1 and 2 here
<row_id>2</row_id>
</selection>

<qualification >
<level>3</level>
<row_id>3</row_id>
</qualification >

<result >
<level>1</level>
<row_id>4</row_id>
<result>


</rule>
</rule_block>

XQuery shoud return row 2 and level 6 as i need to display them as problematic values


Thanks


 
ottawamunda,

here is an XPath you can use and extend
//level[not(
(parent::result) or
 (text()='1' and not(preceding::level)) or
 (text() = preceding::level[1]/text() + 1) or
 (text() = preceding::level[2]/text() + 2)
 )]/following-sibling::row_id

What I did in the predicate is list all the levels that are acceptable, with and or statement
acceptable in my logic are

- levels that are in result
- the first level in the set, if it has a content "1"
- any level that has a content of the previous level plus one
- any level that has a content of two levels ago plus two (if the previous would be in error)
- you can add levels for your other conditions... eg. previous minus one etc.

then I wrap that set of conditions in a not()
basically I then have all the levels that are not correct, this is what you want
from that level I take the following-sibling::row_id... gives you "2" in your example

If you happen to create this XML yourself...
it is very poor modelling to do it this way
using XML's hierarchy based datamodel, you should bring elements that belong together,
together in a container element
You should not rely on the fact that processors take the next element as being part of the same "group"

so this is not good
<level>1</level>
<row_id>1</row_id>

this would be better
<levelinfo>
<level>1</level>
<row_id>1</row_id>
</levelinfo>
but that would break the above XPath logic

this is even better since you don't have to change the XPath logic
<level row_id="1">1</level>

cheers

Geert
Hi Gertone

I am trying to run an XQuery using xml beans

Sample of xml:

rawXML ="<rule><rule_block>  <selection>     <row_id>2</row_id>     <level>2</level>     <logical_operator>      <AND>Text</AND>     </logical_operator>     <selection_parameter>      <job_code>String</job_code>     </selection_parameter>    </selection>    <selection>     <row_id>2</row_id>     <level>2</level>     <logical_operator>      <AND>Text</AND>     </logical_operator>     <selection_parameter>      <job_code>String</job_code>     </selection_parameter>    </selection>    etc………………………

java class file that i am using to validate

package com.ewr.rules.action;

import java.util.ArrayList;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.log4j.Logger;

import noNamespace.RuleDocument;
import noNamespace.Level;

public class UphwValidator {
      static Logger logger = Logger.getLogger(UphwValidator.class);

      public UphwValidator() {
            
      }

      public ArrayList validate (String rawXML) { <-------------getting xml--
            ArrayList errorList = new ArrayList();
            String xNamespace ="declare namespace xs='http://www.w3.org/2001/XMLSchema';";
            String xPath="$this/level";
            String query = xNamespace + xPath ;
            RuleDocument ruleDoc;
            try {
                  
                  /** set validation options*/
                  XmlOptions Xoptions= new XmlOptions();
                  Xoptions.setErrorListener(errorList);
                  Xoptions.setCompileDownloadUrls();
                  Xoptions.setSaveNamespacesFirst();
                  Xoptions.setUseDefaultNamespace();
                  XmlObject levelErrors[];
                  Level myLevel =Level.Factory.newInstance();
                  /** parse the XML */
                  ruleDoc = RuleDocument.Factory.parse(rawXML,Xoptions);
/** validate the XML*/
                  
                  boolean isValid= ruleDoc.validate(Xoptions);
                  logger.info("xml validation returned "+ isValid);
                  

levelErrors= ruleDoc.selectPath(query); <-------------XQUERY--
                  
for(int i=0; i<levelErrors.length; i++){
                        errorList.add(levelErrors[i].toString());
                  }
                  
                  
            } catch (Exception e) {
                  logger.fatal("Error in UPHW validator",e);
                  
            }
            
            return errorList;
      }
      
      
      
      
}

When I use the following declaration I get no errors but 0 nodes are selected

 
String xNamespace ="declare namespace xs='http://www.w3.org/2001/XMLSchema';";
            String xPath="$this/level";
            String query = xNamespace + xPath ;


Gives me an exception on all of the following

String xPath="$this/descendant-or-self::node()";
String xPath="$this/descendant-or-self::node()/child::level";
String xPath="//xs:level";


java.lang.RuntimeException:  Trying XBeans path engine... Trying XQRL... Trying Saxon... FAILED on declare namespace xs='http://www.w3.org/2001/XMLSchema';$this/descendant-or-self::node()
      at org.apache.xmlbeans.impl.store.Path.getCompiledPath(Path.java:131)
      at org.apache.xmlbeans.impl.store.Path.getCompiledPath(Path.java:91)
      at org.apache.xmlbeans.impl.store.Cursor._selectPath(Cursor.java:890)
      at org.apache.xmlbeans.impl.store.Cursor.selectPath(Cursor.java:2622)
      at org.apache.xmlbeans.impl.values.XmlObjectBase.selectPath(XmlObjectBase.java:430)
      at org.apache.xmlbeans.impl.values.XmlObjectBase.selectPath(XmlObjectBase.java:414)
      at com.ewr.rules.action.UphwValidator.validate(UphwValidator.java:56)


how do i write the XQuery for the Xpath that you suggested Xml spy had placed a default namespace xs='http://www.w3.org/2001/XMLSchema' whne i compiled my code thought that pacakage name generated was
import noNamespace.RuleDocument;
import noNamespace.Level;

For validation to work i had to remove all namescpace decelaration from the xml.

i got the tip for XQuery from

http://xmlbeans.apache.org/docs/2.0.0/guide/conSelectingXMLwithXQueryPathXPath.html

Thanks for all you help, looking forwrad to you help

Thanks
if i set

String xNamespace ="declare namespace xs='http://www.w3.org/2001/XMLSchema';";
            String xPath="$this/rule/rule_block/selection/level";
            String query = xNamespace + xPath ;

it works fine i am able to print out following.

<xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment><xml-fragment>2</xml-fragment>

so i guess we need to figure out how to select level without specifing complete path. ie it should select all levels inside (selecetion, qualification, extension, results)

Thanks
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Gertone

One of the reasons the query was not working was that I didn’t have saxon8.jar  and xbean_xpath.jar on my class path. So whenever I used predicate it was giving me error.

All is not well yet!


This one is not returning the expected row id

String xPath="$this//level[not ( (text() = preceding::level[1]/text() + 1) or (text() = preceding::level[1]/text() - 1) )]/preceding-sibling::row_id";


Gives me row id 1 it should be row id 3

If I change the path to

String xPath="$this//level[not ( (text()='1' and not(preceding::level) ) or (text() = preceding::level[1]/text() + 1) or (text() = preceding::level[1]/text() - 1) )]/preceding-sibling::row_id";

Returns nothing



My sample xml is (the one used in application has\n \t $ ^  removed):

<rule >
      <rule_block>
            <selection>
                  <row_id>1</row_id>
                  <level>1</level>
                  <logical_operator>
                        <AND>Text</AND>
                  </logical_operator>
                  <selection_parameter>
                        <job_code>String</job_code>
                  </selection_parameter>
            </selection>
            <qualification>
                  <row_id>2</row_id>
                  <Level>2</Level>
                  <logical_operator>
                        <AND>Text</AND>
                  </logical_operator>
                  <qualification_parameter>
                        <qualification_procedure>
                              <A_Based_on_total_hours>
                                    <Required_hours>2</Required_hours>
                                    <In_weeks>2</In_weeks>
                                    <time_frame>2</time_frame>
                              </A_Based_on_total_hours>
                        </qualification_procedure>
                  </qualification_parameter>
            </qualification>
            <extension>
                  <row_id>2</row_id>
                  <Level>2</Level>
                  <logical_operator>
                        <AND>Text</AND>
                  </logical_operator>
                  <extension_parameter>
                        <Status_change>
                              <reason>String</reason>
                              <Number_of_extension>2</Number_of_extension>
                        </Status_change>
                  </extension_parameter>
            </extension>
            <result>
                  <row_id>2</row_id>
                  <Level>2</Level>
                  <logical_operator>
                        <AND>Text</AND>
                  </logical_operator>
                  <result_parameter>
                        <payment_type>String</payment_type>
                  </result_parameter>
            </result>
      </rule_block>
</rule>








Also the following don’t work they give no result:

String xNamespace ="declare namespace xs='http://www.w3.org/2001/XMLSchema';";
String xPath="$this//level[(ancestor::result)]";  
String xPath="$this//level[parent::result]";

String query = xNamespace + xPath ;


Thanks a lot