从XSD数组创建模式

时间:2016-08-25 23:04:48

标签: java xml xsd schema xsd-validation

我遇到了Java XML验证API的问题。当一组XSD(模式文件)传递给方法 javax.xml.validation.SchemaFactory.newSchema(Source []模式)时,它会抛出以下异常:

org.xml.sax.SAXParseException; src-resolve: Cannot resolve the name 'Report' to a(n) 'type definition' component.
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
    at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:306)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4162)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4141)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(XSDHandler.java:1674)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseNamedElement(XSDElementTraverser.java:405)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseGlobal(XSDElementTraverser.java:242)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseSchemas(XSDHandler.java:1429)
    at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:626)
    at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:613)
    at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:572)
    at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:538)
    at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:252)
    ...

下面显示了用于生成架构的部分代码。

public static Object converToObject(byte[] xmlByteBuffer, int offset, int length, Class<?>... type) throws JAXBException {

    JAXBContext context = JAXBContext.newInstance(type);

    final List<DOMResult> results = new ArrayList<DOMResult>();

    try {
        context.generateSchema(new SchemaOutputResolver() {
            @Override
            public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
                DOMResult result = new DOMResult();
                result.setSystemId(suggestedFileName);

                results.add(result);

                return result;
            }
        });
    } catch (IOException e1) {
        e1.printStackTrace();
    }

    final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

    DOMSource[] sources = new DOMSource[results.size()];

    for (int i = 0; i < results.size(); i++) {
        DOMResult result = results.get(i);
        Node node = result.getNode();

        sources[i] = new DOMSource(node);

    }

    // First Solution: Reverse Order
//  DOMSource[] resortedSources = new DOMSource[results.size()];
//  for(int i=1; i<results.size(); i++){
//      sourcesSorted[results.size()-(i+1)] = sources[i];
//  }

    // Second Solution: Look for the XSD with imports with namespace
    // attribute        
    for (int i = 0; i < results.size(); i++) {
        Node node = sources[i].getNode();

        if(thereIsElementImportWithAttributeNamespace(node)) {
            DOMSource sourceBackup = sources[0];
            sources[0] = sources[i];
            sources[i] = sourceBackup;
            break;
        }
    }


    Schema schema = null;
    try {
        schema = schemaFactory.newSchema(sources);
//      schema = schemaFactory.newSchema(resortedSources);
    } catch (SAXException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Unmarshaller unmarshaller = context.createUnmarshaller();

    unmarshaller.setSchema(schema);
    unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());

    String str;
    try {
        str = new String(xmlByteBuffer, offset, length, "UTF-8");
    } catch (UnsupportedEncodingException uee) {
        try {
            str = new String(xmlByteBuffer, "ISO-8859-1");
        } catch (UnsupportedEncodingException ex) {
            throw new JAXBException(ex);
        }
    }

    StreamSource ss = new StreamSource(new StringReader(str));

    Object obj = unmarshaller.unmarshal(ss);
    return obj;
}

下面显示了传递给方法 newSchema 的真实XSD的打印部分。

schema1.xsd:     

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://service.mw.uw/" version="1.0">
    <xs:import schemaLocation="schema4.xsd"/>
    <xs:element name="Report" type="Report"/>
    ...
</xs:schema>

schema2.xsd:     

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://service.gather.mw.uw/" version="1.0">
    <xs:import schemaLocation="schema4.xsd"/>
    <xs:element name="DataReport" type="DataReport"/>
    ...
</xs:schema>

schema3.xsd:     

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://service.commons.uw/" targetNamespace="http://service.commons.uw/" version="1.0">
    <xs:import schemaLocation="schema4.xsd"/>
    <xs:element name="ApplicationRequest" type="ApplicationRequest"/>
    ...
</xs:schema>

schema4.xsd:     

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://service.commons.uw/" xmlns:ns2="http://service.gather.mw.uw/" version="1.0">
    <xs:import namespace="http://service.commons.uw/" schemaLocation="schema3.xsd"/>
    <xs:import namespace="http://service.gather.mw.uw/" schemaLocation="schema2.xsd"/>
    <xs:complexType name="message">
        ...
    </xs:complexType>
    ...

</xs:schema>

国外传播 SchemaFactory.newSchema 无法处理导入多个XSD的多个XSD。

根据Hedleys,解决方案是在实际导入XSD之前,使用导入的XSD对任何XSD数组进行排序。我遇到了这个解决方案的问题,因为在我的项目中,XSD之间有几个相互导入。那怎么可以排序呢?

此外,根据jtahlborn,另一种解决方案是以相反的顺序使用XSD阵列。这工作得很好!但是,我一直在研究这个问题,因为在那个时候,我只有几个不同的案例来测试这个解决方案是否足够通用!

方法 SchemaFactory.newSchema 的Java文档声明如下:“XML Schema建议的第4.2.3节描述了处理器在这方面的选项。虽然处理器应该在其中保持一致处理JAXP模式源和XML模式导入时,JAXP兼容解析器之间的行为可能会有所不同;特别是,无论schemaLocation中提供的信息如何,解析器都可以选择忽略除给定命名空间的第一个之外的所有命令 ”。因此,基于该语句,我可以通过将包含namespace属性的导入的XSD放置在XSD阵列的第一个位置来解决早期提到的异常。最后,我的问题出现了:

是从XSD数组中读取的第一个XSD的导入中是否存在异常的原因?

我只能用几个不同的案例测试最后的解决方案,但一切都运行得很好!如果有人有更多的测试用例可以尝试,请让我们知道最后一个解决方案是否真的有效。

1 个答案:

答案 0 :(得分:0)

我在任何这些架构文档中都没有看到名为Report(无命名空间)的类型,因此错误消息对我来说似乎完全合法。