简体   繁体   English

在Java中,如何解析xml架构(xsd)以了解在给定元素处的有效内容?

[英]In Java, how do I parse an xml schema (xsd) to learn what's valid at a given element?

I'd like to be able to read in an XML schema (ie xsd) and from that know what are valid attributes, child elements, values as I walk through it. 我希望能够读取XML模式(即xsd),并从中了解当我浏览它时有效的属性,子元素,值。

For example, let's say I have an xsd that this xml will validate against: 例如,假设我有一个xsd,这个xml将验证:

<root>
  <element-a type="something">
    <element-b>blah</element-b>
    <element-c>blahblah</element-c>
  </element-a>
</root>

I've tinkered with several libraries and I can confidently get <root> as the root element. 我已经修改了几个库,我可以自信地将<root>作为根元素。 Beyond that I'm lost. 除此之外,我迷失了。

Given an element I need to know what child elements are required or allowed, attributes, facets, choices, etc. Using the above example I'd want to know that element-a has an attribute type and may have children element-b and element-c ...or must have children element-b and element-c ...or must have one of each...you get the picture I hope. 给定一个元素我需要知道哪些子元素是必需的或允许的,属性,方面,选择等。使用上面的例子,我想知道element-a有一个属性type ,可能有子element-belement-c ...或者必须有子element-belement-c ...或者必须有一个...你得到我希望的图片。

I've looked at numerous libraries such as XSOM, Eclipse XSD, Apache XmlSchema and found they're all short on good sample code. 我看过很多库,比如XSOM,Eclipse XSD,Apache XmlSchema,发现它们都是很好的示例代码。 My search of the Internet has also been unsuccessful. 我对互联网的搜索也没有成功。

Does anyone know of a good example or even a book that demonstrates how to go through an XML schema and find out what would be valid options at a given point in a validated XML document? 有没有人知道一个很好的例子,甚至是一本书,它演示了如何通过XML模式并找出在经过验证的XML文档中给定点的有效选项?

clarification 澄清

I'm not looking to validate a document, rather I'd like to know the options at a given point to assist in creating or editing a document. 我不打算验证文档,而是希望了解给定点的选项以帮助创建或编辑文档。 If I know "I am here" in a document, I'd like to determing what I can do at that point. 如果我在一份文件中知道“我在这里”,我想确定那时我能做些什么。 "Insert one of element A, B, or C" or "attach attribute 'description'". “插入元素A,B或C中的一个”或“附加属性'描述'”。

Many of the solutions for validating XML in java use the JAXB API . 许多用于在java中验证XML的解决方案都使用JAXB API There's an extensive tutorial available here . 有多种选择的教程在这里 The basic recipe for doing what you're looking for with JAXB is as follows: 使用JAXB执行所需操作的基本方法如下:

  1. Obtain or create the XML schema to validate against. 获取或创建要验证的XML模式。
  2. Generate Java classes to bind the XML to using xjc , the JAXB compiler. 生成Java类以将XML绑定到使用xjc (JAXB编译器)。
  3. Write java code to: 编写java代码:
    1. Open the XML content as an input stream. 将XML内容作为输入流打开。
    2. Create a JAXBContext and Unmarshaller 创建一个JAXBContextUnmarshaller
    3. Pass the input stream to the Unmarshaller 's unmarshal method. 将输入流传递给Unmarshallerunmarshal方法。

The parts of the tutorial you can read for this are: 您可以阅读的教程部分是:

  1. Hello, world 你好,世界
  2. Unmarshalling XML 解组XML

This is a good question. 这是一个很好的问题。 Although, it is old, I did not find an acceptable answer. 虽然它已经老了,但我找不到可接受的答案。 The thing is that the existing libraries I am aware of ( XSOM , Apache XmlSchema ) are designed as object models. 问题是我所知道的现有库( XSOMApache XmlSchema )被设计为对象模型。 The implementors did not have the intention to provide any utility methods — you should consider implement them yourself using the provided object model. 实现者无意提供任何实用方法 - 您应该考虑使用提供的对象模型自己实现它们。

Let's see how querying context-specific elements can be done by the means of Apache XmlSchema. 让我们看看如何通过Apache XmlSchema来查询特定于上下文的元素。

You can use their tutorial as a starting point. 您可以使用他们的教程作为起点。 In addition, Apache CFX framework provides the XmlSchemaUtils class with lots of handy code examples. 此外,Apache CFX框架为XmlSchemaUtils类提供了许多方便的代码示例。

First of all, read the XmlSchemaCollection as illustrated by the library's tutorial: 首先,阅读库的教程所示的XmlSchemaCollection

XmlSchemaCollection xmlSchemaCollection = new XmlSchemaCollection();
xmlSchemaCollection.read(inputSource, new ValidationEventHandler());

Now, XML Schema defines two kinds of data types: 现在,XML Schema定义了两种数据类型:

  • Simple types 简单的类型
  • Complex types 复杂的类型

Simple types are represented by the XmlSchemaSimpleType class. 简单类型由XmlSchemaSimpleType类表示。 Handling them is easy. 处理它们很容易。 Read the documentation: https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaSimpleType.html . 阅读文档: https//ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaSimpleType.html But let's see how to handle complex types. 但是让我们看看如何处理复杂类型。 Let's start with a simple method: 让我们从一个简单的方法开始:

@Override
public List<QName> getChildElementNames(QName parentElementName) {
    XmlSchemaElement element = xmlSchemaCollection.getElementByQName(parentElementName);
    XmlSchemaType type = element != null ? element.getSchemaType() : null;

    List<QName> result = new LinkedList<>();
    if (type instanceof XmlSchemaComplexType) {
        addElementNames(result, (XmlSchemaComplexType) type);
    }
    return result;
}

XmlSchemaComplexType may stand for both real type and for the extension element. XmlSchemaComplexType可以代表实型和extension元素。 Please see the public static QName getBaseType(XmlSchemaComplexType type) method of the XmlSchemaUtils class. 请参阅public static QName getBaseType(XmlSchemaComplexType type)的方法XmlSchemaUtils类。

private void addElementNames(List<QName> result, XmlSchemaComplexType type) {
    XmlSchemaComplexType baseType = getBaseType(type);
    XmlSchemaParticle particle = baseType != null ? baseType.getParticle() : type.getParticle();

    addElementNames(result, particle);
}

When you handle XmlSchemaParticle , consider that it can have multiple implementations. 处理XmlSchemaParticle ,请考虑它可以有多个实现。 See: https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaParticle.html 请参阅: https//ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaParticle.html

private void addElementNames(List<QName> result, XmlSchemaParticle particle) {
    if (particle instanceof XmlSchemaAny) {

    } else if (particle instanceof XmlSchemaElement) {

    } else if (particle instanceof XmlSchemaGroupBase) {

    } else if (particle instanceof XmlSchemaGroupRef) {

    }
}

The other thing to bear in mind is that elements can be either abstract or concrete. 另外要记住的是元素可以是抽象的也可以是具体的。 Again, the JavaDocs are the best guidance. 同样,JavaDocs是最好的指导。

I see you have tried Eclipse XSD. 我看到你尝试过Eclipse XSD。 Have you tried Eclipse Modeling Framework (EMF)? 您是否尝试过Eclipse Modeling Framework(EMF)? You can: 您可以:

Generating an EMF Model using XML Schema (XSD) 使用XML Schema(XSD)生成EMF模型

Create a dynamic instance from your metamodel (3.1 With the dynamic instance creation tool) 从元模型创建动态实例 (3.1使用动态实例创建工具)

This is for exploring the xsd. 这是为了探索xsd。 You can create the dynamic instance of the root element then you can right click the element and create child element. 您可以创建根元素的动态实例,然后可以右键单击元素并创建子元素。 There you will see what the possible children element and so on. 在那里你会看到可能的子元素等等。

As for saving the created EMF model to an xml complied xsd: I have to look it up. 至于将创建的EMF模型保存到xml编译的xsd:我必须查找它。 I think you can use JAXB for that ( How to use EMF to read XML file? ). 我认为您可以使用JAXB( 如何使用EMF读取XML文件? )。


Some refs: 一些参考:

EMF: Eclipse Modeling Framework, 2nd Edition (written by creators) EMF:Eclipse Modeling Framework,第2版 (由创作者编写)
Eclipse Modeling Framework (EMF) Eclipse Modeling Framework(EMF)
Discover the Eclipse Modeling Framework (EMF) and Its Dynamic Capabilities 发现Eclipse Modeling Framework(EMF)及其动态功能
Creating Dynamic EMF Models From XSDs and Loading its Instances From XML as SDOs 从XSD创建动态EMF模型并将其实例从XML加载为SDO

This is a fairly complete sample on how to parse an XSD using XSOM: 这是一个关于如何使用XSOM解析XSD的相当完整的示例:

import java.io.File;
import java.util.Iterator;
import java.util.Vector;

import org.xml.sax.ErrorHandler;

import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroupDecl;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSRestrictionSimpleType;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSSimpleType;
import com.sun.xml.xsom.XSTerm;
import com.sun.xml.xsom.impl.Const;
import com.sun.xml.xsom.parser.XSOMParser;
import com.sun.xml.xsom.util.DomAnnotationParserFactory;

public class XSOMNavigator
{
    public static class SimpleTypeRestriction
    {
        public String[] enumeration = null;
        public String   maxValue    = null;
        public String   minValue    = null;
        public String   length      = null;
        public String   maxLength   = null;
        public String   minLength   = null;
        public String[] pattern     = null;
        public String   totalDigits = null;
        public String   fractionDigits = null;
        public String   whiteSpace = null;

        public String toString()
        {
            String enumValues = "";
            if (enumeration != null)
            {
                for(String val : enumeration)
                {
                    enumValues += val + ", ";
                }
                enumValues = enumValues.substring(0, enumValues.lastIndexOf(','));
            }

            String patternValues = "";
            if (pattern != null)
            {
                for(String val : pattern)
                {
                    patternValues += "(" + val + ")|";
                }
                patternValues = patternValues.substring(0, patternValues.lastIndexOf('|'));
            }
            String retval = "";
            retval += minValue    == null ? "" : "[MinValue  = "   + minValue      + "]\t";
            retval += maxValue    == null ? "" : "[MaxValue  = "   + maxValue      + "]\t";
            retval += minLength   == null ? "" : "[MinLength = "   + minLength     + "]\t";
            retval += maxLength   == null ? "" : "[MaxLength = "   + maxLength     + "]\t";
            retval += pattern     == null ? "" : "[Pattern(s) = "  + patternValues + "]\t";
            retval += totalDigits == null ? "" : "[TotalDigits = " + totalDigits   + "]\t";
            retval += fractionDigits == null ? "" : "[FractionDigits = " + fractionDigits   + "]\t";
            retval += whiteSpace  == null ? "" : "[WhiteSpace = "      + whiteSpace        + "]\t";          
            retval += length      == null ? "" : "[Length = "      + length        + "]\t";          
            retval += enumeration == null ? "" : "[Enumeration Values = "      + enumValues    + "]\t";

            return retval;
        }
    }

    private static void initRestrictions(XSSimpleType xsSimpleType, SimpleTypeRestriction simpleTypeRestriction)
    {
        XSRestrictionSimpleType restriction = xsSimpleType.asRestriction();
        if (restriction != null)
        {
            Vector<String> enumeration = new Vector<String>();
            Vector<String> pattern     = new Vector<String>();

            for (XSFacet facet : restriction.getDeclaredFacets())
            {
                if (facet.getName().equals(XSFacet.FACET_ENUMERATION))
                {
                    enumeration.add(facet.getValue().value);
                }
                if (facet.getName().equals(XSFacet.FACET_MAXINCLUSIVE))
                {
                    simpleTypeRestriction.maxValue = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MININCLUSIVE))
                {
                    simpleTypeRestriction.minValue = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MAXEXCLUSIVE))
                {
                    simpleTypeRestriction.maxValue = String.valueOf(Integer.parseInt(facet.getValue().value) - 1);
                }
                if (facet.getName().equals(XSFacet.FACET_MINEXCLUSIVE))
                {
                    simpleTypeRestriction.minValue = String.valueOf(Integer.parseInt(facet.getValue().value) + 1);
                }
                if (facet.getName().equals(XSFacet.FACET_LENGTH))
                {
                    simpleTypeRestriction.length = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MAXLENGTH))
                {
                    simpleTypeRestriction.maxLength = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_MINLENGTH))
                {
                    simpleTypeRestriction.minLength = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_PATTERN))
                {
                    pattern.add(facet.getValue().value);
                }
                if (facet.getName().equals(XSFacet.FACET_TOTALDIGITS))
                {
                    simpleTypeRestriction.totalDigits = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_FRACTIONDIGITS))
                {
                    simpleTypeRestriction.fractionDigits = facet.getValue().value;
                }
                if (facet.getName().equals(XSFacet.FACET_WHITESPACE))
                {
                    simpleTypeRestriction.whiteSpace = facet.getValue().value;
                }
            }
            if (enumeration.size() > 0)
            {
                simpleTypeRestriction.enumeration = enumeration.toArray(new String[] {});
            }
            if (pattern.size() > 0)
            {
                simpleTypeRestriction.pattern = pattern.toArray(new String[] {});
            }
        }
    }

    private static void printParticle(XSParticle particle, String occurs, String absPath, String indent)
    {
        boolean repeats = particle.isRepeated();
        occurs = "  MinOccurs = " + particle.getMinOccurs() + ", MaxOccurs = " + particle.getMaxOccurs() + ", Repeats = " + Boolean.toString(repeats);
        XSTerm term = particle.getTerm();
        if (term.isModelGroup())
        {
            printGroup(term.asModelGroup(), occurs, absPath, indent);    
        }
        else if(term.isModelGroupDecl())
        {
            printGroupDecl(term.asModelGroupDecl(), occurs, absPath, indent);    
        }
        else if (term.isElementDecl())
        {
            printElement(term.asElementDecl(), occurs, absPath, indent);
        }
    }

    private static void printGroup(XSModelGroup modelGroup, String occurs, String absPath, String indent)
    {
        System.out.println(indent + "[Start of Group " + modelGroup.getCompositor() + occurs + "]" );
        for (XSParticle particle : modelGroup.getChildren())
        {
            printParticle(particle, occurs, absPath, indent + "\t");
        }
        System.out.println(indent + "[End of Group " + modelGroup.getCompositor() + "]");
    }

    private static void printGroupDecl(XSModelGroupDecl modelGroupDecl, String occurs, String absPath, String indent)
    {
        System.out.println(indent + "[GroupDecl " + modelGroupDecl.getName() + occurs + "]");
        printGroup(modelGroupDecl.getModelGroup(), occurs, absPath, indent);
    }

    private static void printComplexType(XSComplexType complexType, String occurs, String absPath, String indent)
    {
        System.out.println();
        XSParticle particle = complexType.getContentType().asParticle();
        if (particle != null)
        {
            printParticle(particle, occurs, absPath, indent);
        }
    }

    private static void printSimpleType(XSSimpleType simpleType, String occurs, String absPath, String indent)
    {
        SimpleTypeRestriction restriction = new SimpleTypeRestriction();
        initRestrictions(simpleType, restriction);
        System.out.println(restriction.toString());
    }

    public static void printElement(XSElementDecl element, String occurs, String absPath, String indent)
    {
        absPath += "/" + element.getName();
        String typeName = element.getType().getBaseType().getName();
        if(element.getType().isSimpleType() && element.getType().asSimpleType().isPrimitive())
        {
            // We have a primitive type - So use that instead
            typeName = element.getType().asSimpleType().getPrimitiveType().getName();
        }

        boolean nillable = element.isNillable();
        System.out.print(indent + "[Element " + absPath + "   " + occurs + "] of type [" + typeName + "]" + (nillable ? " [nillable] " : ""));
        if (element.getType().isComplexType())
        {
            printComplexType(element.getType().asComplexType(), occurs, absPath, indent);
        }
        else
        {
            printSimpleType(element.getType().asSimpleType(), occurs, absPath, indent);
        }
    }

    public static void printNameSpace(XSSchema s, String indent)
    {
        String nameSpace = s.getTargetNamespace();

        // We do not want the default XSD namespaces or a namespace with nothing in it
        if(nameSpace == null || Const.schemaNamespace.equals(nameSpace) || s.getElementDecls().isEmpty())
        {
            return;
        }

        System.out.println("Target namespace: " + nameSpace);
        Iterator<XSElementDecl> jtr = s.iterateElementDecls();
        while (jtr.hasNext())
        {
            XSElementDecl e = (XSElementDecl) jtr.next();

            String occurs  = "";
            String absPath = "";

            XSOMNavigator.printElement(e, occurs, absPath,indent);
            System.out.println();
        }
    }

    public static void xsomNavigate(File xsdFile)
    {
        ErrorHandler    errorHandler    = new ErrorReporter(System.err);
        XSSchemaSet     schemaSet = null;

        XSOMParser parser = new XSOMParser();
        try
        {
            parser.setErrorHandler(errorHandler);
            parser.setAnnotationParser(new DomAnnotationParserFactory());
            parser.parse(xsdFile);
            schemaSet = parser.getResult();
        }
        catch (Exception exp)
        {
            exp.printStackTrace(System.out);
        }

        if(schemaSet != null)
        {
            // iterate each XSSchema object. XSSchema is a per-namespace schema.
            Iterator<XSSchema> itr = schemaSet.iterateSchema();
            while (itr.hasNext())
            {
                XSSchema s = (XSSchema) itr.next();
                String indent  = "";
                printNameSpace(s, indent);
            }
        }
    }

    public static void printFile(String fileName)
    {
        File fileToParse = new File(fileName);
        if (fileToParse != null && fileToParse.canRead())
        {
            xsomNavigate(fileToParse);
        }
    }
}

And for your Error Reporter use: 并为您的错误报告使用:

import java.io.OutputStream;
import java.io.PrintStream;
import java.text.MessageFormat;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class ErrorReporter implements ErrorHandler {

    private final PrintStream out;

    public ErrorReporter( PrintStream o ) { this.out = o; }
    public ErrorReporter( OutputStream o ) { this(new PrintStream(o)); }

    public void warning(SAXParseException e) throws SAXException {
        print("[Warning]",e);
    }

    public void error(SAXParseException e) throws SAXException {
        print("[Error  ]",e);
    }

    public void fatalError(SAXParseException e) throws SAXException {
        print("[Fatal  ]",e);
    }

    private void print( String header, SAXParseException e ) {
        out.println(header+' '+e.getMessage());
        out.println(MessageFormat.format("   line {0} at {1}",
            new Object[]{
                Integer.toString(e.getLineNumber()),
                e.getSystemId()}));
    }
}

For your main use: 主要用途:

public class WDXSOMParser { 公共类WDXSOMParser {

    public static void main(String[] args)
    {
        String fileName = null;
        if(args != null && args.length > 0 && args[0] != null)
            fileName = args[0];
        else
        fileName = "C:\\xml\\CollectionComments\\CollectionComment1.07.xsd";
        //fileName = "C:\\xml\\PropertyListingContractSaleInfo\\PropertyListingContractSaleInfo.xsd";
        //fileName = "C:\\xml\\PropertyPreservation\\PropertyPreservation.xsd";

        XSOMNavigator.printFile(fileName);
    }
}

It's agood bit of work depending on how compex your xsd is but basically. 这取决于你的xsd是如何复杂的,但基本上是很好的工作。

if you had 如果你有

<Document>
<Header/>
<Body/>
<Document>

And you wanted to find out where were the alowable children of header you'd (taking account of namespaces) Xpath would have you look for '/element[name="Document"]/element[name="Header"]' 并且你想找出你可以使用标题的可以使用的孩子的位置(考虑名称空间)Xpath会让你找到'/ element [name =“Document”] / element [name =“Header”]'

After that it depends on how much you want to do. 之后,这取决于你想做多少。 You might find it easier to write or find something that loads an xsd into a DOM type structure. 您可能会发现编写或查找将xsd加载到DOM类型结构中的内容更容易。 Course you are going to possibly find all sorts of things under that elment in xsd, choice, sequence, any, attributes, complexType, SimpleContent, annotation. 当然,你可能会在xsd,choice,sequence,any,attributes,complexType,SimpleContent,annotation中找到各种各样的东西。

Loads of time consuming fun. 充满乐趣的时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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