3

I am using Apache XMLSchema framework to parse and get the elements of an XSD. Now I need to associate a XPath string with each of those elements. Can some one give any Idea on how can I do this. Any existing algorithm or framework that does it?

Example:

<?xml version="1.0" encoding="ISO-8859-1" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:simpleType name="stringtype"> <xs:restriction base="xs:string"/> </xs:simpleType> <xs:simpleType name="inttype"> <xs:restriction base="xs:positiveInteger"/> </xs:simpleType> <xs:simpleType name="dectype"> <xs:restriction base="xs:decimal"/> </xs:simpleType> <xs:simpleType name="orderidtype"> <xs:restriction base="xs:string"> <xs:pattern value="[0-9]{6}"/> </xs:restriction> </xs:simpleType> <xs:complexType name="shiptotype"> <xs:sequence> <xs:element name="name" type="stringtype"/> <xs:element name="address" type="stringtype"/> <xs:element name="city" type="stringtype"/> <xs:element name="country" type="stringtype"/> </xs:sequence> </xs:complexType> <xs:complexType name="itemtype"> <xs:sequence> <xs:element name="title" type="stringtype"/> <xs:element name="note" type="stringtype" minOccurs="0"/> <xs:element name="quantity" type="inttype"/> <xs:element name="price" type="dectype"/> </xs:sequence> </xs:complexType> <xs:complexType name="shipordertype"> <xs:sequence> <xs:element name="orderperson" type="stringtype"/> <xs:element name="shipto" type="shiptotype"/> <xs:element name="item" maxOccurs="unbounded" type="itemtype"/> </xs:sequence> <xs:attribute name="orderid" type="orderidtype" use="required"/> </xs:complexType> <xs:element name="shiporder" type="shipordertype"/> </xs:schema> 

XPath for

orderperson-> ./orderperson name-> ./shipto/name etc 

4 Answers 4

4

If the schema allows recursive structures, or if it contains wildcards, then this is quite a tricky problem, and you will need to specify your requirements much more precisely. For relatively simple non-recursive schemas it should be much more straightforward, though you will need to supply extra information such as what the root element name is.

Sign up to request clarification or add additional context in comments.

Comments

1

There is no out of the box solution for what you want; you will have to write your own, using the visitor pattern. Alternatively, if this is rather a design time artifact that you generate for runtime use as opposed to dynamically at runtime, you could use a solution similar to this SO post.

Comments

1

I think this might help:

import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Stack; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; /** * SAX handler that creates and prints XPath expressions for each element encountered. * * The algorithm is not infallible, if elements appear on different levels in the hierarchy. * Something like the following is an example: * - <elemA/> * - <elemA/> * - <elemB/> * - <elemA/> * - <elemC> * - <elemB/> * - </elemC> * * will report * * //elemA[0] * //elemA[1] * //elemB[0] * //elemA[2] * //elemC[0] * //elemC[0]/elemB[1] (this is wrong: should be //elemC[0]/elemB[0] ) * * It also ignores namespaces, and thus treats <foo:elemA> the same as <bar:elemA>. */ public class SAXCreateXPath extends DefaultHandler { // map of all encountered tags and their running count private Map<String, Integer> tagCount; // keep track of the succession of elements private Stack<String> tags; // set to the tag name of the recently closed tag String lastClosedTag; /** * Construct the XPath expression */ private String getCurrentXPath() { String str = "//"; boolean first = true; for (String tag : tags) { if (first) str = str + tag; else str = str + "/" + tag; str += "["+tagCount.get(tag)+"]"; first = false; } return str; } @Override public void startDocument() throws SAXException { tags = new Stack(); tagCount = new HashMap<String, Integer>(); } @Override public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { boolean isRepeatElement = false; if (tagCount.get(localName) == null) { tagCount.put(localName, 0); } else { tagCount.put(localName, 1 + tagCount.get(localName)); } if (lastClosedTag != null) { // an element was recently closed ... if (lastClosedTag.equals(localName)) { // ... and it's the same as the current one isRepeatElement = true; } else { // ... but it's different from the current one, so discard it tags.pop(); } } // if it's not the same element, add the new element and zero count to list if (! isRepeatElement) { tags.push(localName); } System.out.println(getCurrentXPath()); lastClosedTag = null; } @Override public void endElement (String uri, String localName, String qName) throws SAXException { // if two tags are closed in succession (without an intermediate opening tag), // then the information about the deeper nested one is discarded if (lastClosedTag != null) { tags.pop(); } lastClosedTag = localName; } public static void main (String[] args) throws Exception { if (args.length < 1) { System.err.println("Usage: SAXCreateXPath <file.xml>"); System.exit(1); } // Create a JAXP SAXParserFactory and configure it SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); spf.setValidating(false); // Create a JAXP SAXParser SAXParser saxParser = spf.newSAXParser(); // Get the encapsulated SAX XMLReader XMLReader xmlReader = saxParser.getXMLReader(); // Set the ContentHandler of the XMLReader xmlReader.setContentHandler(new SAXCreateXPath()); String filename = args[0]; String path = new File(filename).getAbsolutePath(); if (File.separatorChar != '/') { path = path.replace(File.separatorChar, '/'); } if (!path.startsWith("/")) { path = "/" + path; } // Tell the XMLReader to parse the XML document xmlReader.parse("file:"+path); } } 

Credits: http://www.coderanch.com/how-to/java/SAXCreateXPath

Comments

0

Reading some where between the lines, I am making an attempt to answer this. You can have a look at JXPath which allows you to use XPath expressions to traverse Object graphs in Java. I have a hunch that this is what you are trying to achieve. I may be mistaken though :)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.