【问题标题】:In Java, how do I parse an xml schema (xsd) to learn what's valid at a given element?在 Java 中,如何解析 xml 模式 (xsd) 以了解给定元素的有效内容?
【发布时间】:2012-01-07 03:05:56
【问题描述】:

我希望能够读取 XML 模式(即 xsd),并从中了解什么是有效属性、子元素和值。

例如,假设我有一个 xsd,此 xml 将对其进行验证:

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

我已经修改了几个库,我可以自信地将&lt;root&gt; 作为根元素。除此之外,我迷路了。

给定一个元素,我需要知道需要或允许哪些子元素、属性、方面、选择等。使用上面的示例,我想知道 element-a 有一个属性 type 并且可能有子元素element-belement-c...或者必须有孩子 element-belement-c...或者必须各有一个...我希望你能得到照片。

我查看了许多库,例如 XSOM、Eclipse XSD、Apache XmlSchema,发现它们都缺少好的示例代码。我在网上搜索也没有成功。

有没有人知道一个很好的例子甚至一本书来演示如何通过 XML 模式并找出在经过验证的 XML 文档中给定点的有效选项?

澄清

我不是要验证文档,而是想知道给定点的选项以帮助创建或编辑文档。如果我在文档中知道“我在这里”,我想确定那时我能做什么。 “插入元素 A、B 或 C 之一”或“附加属性‘描述’”。

【问题讨论】:

  • 您想要工具来帮助探索 xsd 还是想要以编程方式处理事物?
  • c) 以上所有。我想读入 xsd 并能够在经过验证的(使用 xsd)xml 文档中向用户(我,现在无论如何)呈现给定点的有效选项。 xml 的“选择你自己的冒险”。
  • stackoverflow.com/questions/1435452/… - 相关的,有点
  • 我认为你想要的和某人在编写从 XML Schema 生成示例 XML 或数据输入 UI 的工具时所需要的一样;也许这种平行可以帮助引导答案?

标签: java xsd


【解决方案1】:

这是个好问题。虽然它很旧,但我没有找到可接受的答案。问题是我知道的现有库(XSOMApache XmlSchema)被设计为对象模型。实现者无意提供任何实用方法——您应该考虑使用提供的对象模型自己实现它们。

让我们看看如何通过 Apache XmlSchema 查询特定于上下文的元素。

您可以使用他们的tutorial 作为起点。此外,Apache CFX 框架为XmlSchemaUtils 类提供了大量方便的代码示例。

首先,阅读XmlSchemaCollection,如图书馆的教程所示:

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

现在,XML Schema 定义了两种数据类型:

  • 简单类型
  • 复杂类型

简单类型由XmlSchemaSimpleType 类表示。处理它们很容易。阅读文档:https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaSimpleType.html。但是让我们看看如何处理复杂类型。让我们从一个简单的方法开始:

@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 可以代表真实类型和extension 元素。请参阅XmlSchemaUtils 类的public static QName getBaseType(XmlSchemaComplexType type) 方法。

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

    addElementNames(result, particle);
}

当您处理XmlSchemaParticle 时,请考虑它可以有多个实现。见: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) {

    }
}

要记住的另一件事是元素可以是抽象的也可以是具体的。同样,JavaDocs 是最好的指导。

【讨论】:

  • 您好,非常感谢您的回答。在查看此答案后,我尝试使用 XmlSchema Core 实现 XSD 解析器,但我不确定如何获取所有子元素及其类型和其他相关信息。如果可能的话,您能否解释一下如何获取所有子元素的复杂性及其类型和其他相关信息?提前致谢。
  • 我在使用 APACHE XMLSCHEMA CORE 库解析 XSD 时有点卡住了。我试图解决这个问题将近 1 天。我在这里发布了我的问题:stackoverflow.com/questions/66874536/… 如果你有机会请检查它并提供你的解决方案。提前非常感谢。
【解决方案2】:

在 java 中验证 XML 的许多解决方案都使用 JAXB APIhere 提供了大量教程。使用 JAXB 完成所需工作的基本方法如下:

  1. 获取或创建要验证的 XML 架构。
  2. 使用 JAXB 编译器 xjc 生成 Java 类以将 XML 绑定到。
  3. 将 java 代码写入:
    1. 将 XML 内容作为输入流打开。
    2. 创建JAXBContextUnmarshaller
    3. 将输入流传递给Unmarshallerunmarshal方法。

您可以阅读的教程部分是:

  1. Hello, world
  2. Unmarshalling XML

【讨论】:

  • 对不起,我认为我没有很好地解释自己。我已经对我原来的问题进行了澄清。我已经可以验证和编组/解组 XML - 我正在寻找创建一些东西来帮助我编辑或创建文档。我对Jaxe 进行了一些修改,但我真的需要一点理解,而不是一个独立的工具。
  • 这个答案最接近我最终所做的。不过,我发现Apache XMLBeans 功能强大得多,而不是使用 xsj,而且由于示例代码,它非常易于使用。
  • 很高兴这可以帮助你。我要提到的一件事是,与 JAXB 生成的类相比,我发现 XMLBeans 相当慢,但这是几年前(大约 2006 年),所以是 YMMV。
【解决方案3】:

我看到您已经尝试过 Eclipse XSD。您是否尝试过 Eclipse 建模框架 (EMF)?你可以:

Generating an EMF Model using XML Schema (XSD)

Create a dynamic instance from your metamodel(3.1带动态实例创建工具)

这是为了探索 xsd。您可以创建根元素的动态实例,然后您可以右键单击该元素并创建子元素。在那里您将看到可能的子元素等等。

至于将创建的 EMF 模型保存到 xml 编译的 xsd:我必须查找它。我认为您可以为此使用 JAXB (How to use EMF to read XML file?)。


一些参考:

EMF: Eclipse Modeling Framework, 2nd Edition(由创作者撰写)
Eclipse Modeling Framework (EMF)
Discover the Eclipse Modeling Framework (EMF) and Its Dynamic Capabilities
Creating Dynamic EMF Models From XSDs and Loading its Instances From XML as SDOs

【讨论】:

  • 感谢您的出色指导。很明显,我有一些阅读要做。
【解决方案4】:

这是一个关于如何使用 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);
        }
    }
}

并供您的错误报告器使用:

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

主要用途:

公共类 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);
    }
}

【讨论】:

    【解决方案5】:

    这取决于您的 xsd 的复杂程度,但基本上是一项很好的工作。

    如果你有

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

    并且您想找出您想要的标头的可允许子代在哪里(考虑到命名空间) Xpath 会让你寻找 '/element[name="Document"]/element[name="Header"]'

    之后,这取决于您想做什么。您可能会发现编写或找到将 xsd 加载到 DOM 类型结构中的东西更容易。 当然你可能会在 xsd 的那个元素下找到各种各样的东西,选择,序列,任何,属性,复杂类型,简单内容,注释。

    大量耗时的乐趣。

    【讨论】:

    • 看起来@Paul Morie 正在为您指出一个现有的实现,如果您不觉得这种事情像我一样有趣的话。
    • 谢谢。我稍微澄清了我的问题。我希望从现有的东西开始,而不是创建解析器。我想我可以拆开一个验证器,看看如何根据模式验证 xml 文档。我没想到我正在尝试做的事情会如此不同寻常。
    • 你的要求并不罕见,我是。我们从 xsds,或者更准确地说是从 xsds 派生的实体树中自动创建表单控件,最近我一直在使用它们和可验证属性包的动态类​​型,所以它处于我的最前沿思考。
    • 我认为表单控件的自动创建问题是一个有趣的问题,我希望我现在有更多的时间来探索它。不幸的是,我认为我必须针对特定情况而不是一般情况进行编程。我必须将此添加到我的“有时间时开始的开源项目”列表中。没有易于使用的工具来从 xsd 到一个好的 UI 要么是一个明显的漏洞,要么是一个没有问题的解决方案,因为每个 xsd 最好为每个 xsd 手动创建一些东西,而不是自动创建它。感谢您的洞察力!
    【解决方案6】:
    猜你喜欢
    • 2014-11-19
    • 1970-01-01
    • 2011-04-05
    • 2022-06-23
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 2013-08-01
    • 2016-01-03
    相关资源
    最近更新 更多