简体   繁体   中英

How to generate xpath from xsd?

How can I generate xpath from an xsd? XSD validates an xml. I am working in a project where I am generating a sample XML from the xsd using java and then generating xpath from that XML. If there is any way to generate xpath directly from xsd please let me know.

This might be of use:

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);
    }
}

There are a number of problems with such tools:

The XPath expression generated rarely is a good one. No such tool will produce meaningful predicates beyond position information.

There is no tool (to my knowledge) that would generate an XPath expression that selects exactly a set of selected nodes.

Apart from this, such tools used without learning XPath are really harmful -- they support ignorance.

I would recommend serious learning of XPath using books and other resources such as following.

https://stackoverflow.com/questions/339930/any-good-xslt-tutorial-book-blog-site-online/341589#341589

See the following answer for more information..

Is there an online tester for xPath selectors?

I've been working on a little library to do just this, though for larger and more complex schemas, there are issues you will need to address on a case-by-case basis (eg, filters for certain nodes). See https://stackoverflow.com/a/45020739/3096687 for a description of the solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM