0

在 JAXB 中是否可以在解组期间仅验证一个xsd:any元素并且不再钻取?

用例是我有两个 XML Schema 文件,一个定义信封(在某些时候包括一个xsd:any元素),另一个定义有效负载模式(用于xsd:any元素)。由于实际上可能有许多不同类型的有效负载 - 未来还会有更多 - 我已经构建了我的代码以使用两步解组,如对这个 SO question的回答中所建议的那样。

所以我有一个库,它只在不查看有效负载的情况下解组和验证信封。问题是使用 JAXB 我无法想出一种方法来仅setSchema使用Unmarshaller. 验证失败,因为它找不到有效负载的模式,但我无法在信封处理jar中添加这些模式,因为信封应该与有效负载无关。因此,我无法以与有效负载无关的方式在其自己的库中实现信封处理逻辑。

下面是一个最小的具体示例。当代码运行时,它会失败并显示:

 [java] Caused by: org.xml.sax.SAXParseException; lineNumber: 8; columnNumber: 23; cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'b:person'.

信封 - A.XSD

<?xml version="1.0" encoding="UTF-8"?>
<schema elementFormDefault="qualified" 
        xmlns               ="http://www.w3.org/2001/XMLSchema"
        xmlns:a             ="http://www.example.org/A"
        targetNamespace ="http://www.example.org/A">
       <element name="someType" type="a:SomeType"></element>
        <complexType name="SomeType">
            <sequence>
                <any namespace="##other" processContents="strict"/>
            </sequence>
        </complexType>
</schema>

有效载荷 - B.XSD

<?xml version="1.0" encoding="UTF-8"?>
<schema elementFormDefault="qualified"
    xmlns          ="http://www.w3.org/2001/XMLSchema"
    xmlns:b        ="http://www.example.org/B"
    targetNamespace="http://www.example.org/B"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <element name="person" type="b:PersonType"></element>
    <complexType name="PersonType">
        <sequence>
                <element name="firstName" type="string"/>
                <element name="lastName"  type="string"/>
        </sequence>
    </complexType>
  </schema>

XML 实例 - ab.xml

<?xml version="1.0" encoding="UTF-8"?>
<a:someType xmlns:a="http://www.example.org/A"
        xmlns:b="http://www.example.org/B"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.example.org/A A.xsd
                            http://www.example.org/B B.xsd">
        <b:person>
            <b:firstName>Mary</b:firstName>
            <b:lastName>Bones</b:lastName>
        </b:person>
</a:someType>

JAXB 代码

public static void main (String args[]) throws JAXBException, FileNotFoundException, SAXException {
    final Schema SCHEMA_A = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new File("A.xsd"));
    JAXBContext jc = JAXBContext.newInstance("example.a");
    Unmarshaller u = jc.createUnmarshaller();
    u.setSchema(SCHEMA_A);
    JAXBElement<SomeType> element = (JAXBElement<SomeType>) u.unmarshal( new FileInputStream( "ab.xml" ) );
}
4

2 回答 2

1

我无法使用 JAXB 实现这种部分验证。但是,使用javax.xml.validation.ValidatorI 是为了验证外部信封并在使用自定义到达有效负载元素时抑制异常org.xml.sax.ErrorHandler。该解决方案根本不能令人满意,因为它依赖于异常消息的比较来抑制异常(类的getColumnNumbergetLineNumber方法SAXParseException不能用作发生错误的确切行和列不是固定的 XML 实例文档) .

我粘贴了下面的代码,但就像我说的那样,我根本不喜欢消息比较抑制逻辑,因为我必须预测所有可能的任何有效负载元素(因为元素名称出现在消息中)。有没有更好的办法?

class CustomErrorHandler implements ErrorHandler {

    @Override
    public void warning(SAXParseException exc) {
        throw exc;
    }

    @Override
    public void error(SAXParseException exc) throws SAXParseException {
        if (exc.getMessage().equals("cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'b:person'."))
            ; // suppress
        else
            throw exc;
    }

    @Override
    public void fatalError(SAXParseException exc) throws SAXParseException {
        throw exc;
    }
}

public class FooMain {


    public static void main (String args[]) throws JAXBException, FileNotFoundException, SAXException, IOException  {
        String xmlFileName = "ab.xml";
        final Schema SCHEMA_A = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new File("A.xsd"));
        SAXSource source = new SAXSource(new InputSource(xmlFileName));
        Validator validator = SCHEMA_A.newValidator();
        validator.setErrorHandler(new CustomErrorHandler());
        validator.validate(source);
        JAXBContext jc = JAXBContext.newInstance("example.a");
        Unmarshaller u = jc.createUnmarshaller();
        // u.setSchema(SCHEMA_A); // not possible to partially validate using this method
        JAXBElement<SomeType> element = (JAXBElement<SomeType>) u.unmarshal( new FileInputStream( xmlFileName ) );
    }
}
于 2013-06-09T15:48:14.210 回答
0

匹配的通配符是严格的,但找不到元素“b:person”的声明。

此消息表示processContents="strict"在您的架构中使用(默认)声明了任何元素。b:person验证器应处理任何元素的内容,但缺少元素声明。

有两种方法可以处理这种情况:

  1. 确保提供给验证器的模式集包括b:person元素声明 - 可以提供多个模式源到SchemaFactory.newSchema. 在这种情况下,Validator 将验证完整的 XML,包括任何元素的内容。

  2. 或 - 通过指定属性processContents="lax"processContents="skip"在您的模式中为任何元素声明放宽内容处理规则,以便执行部分验证,如XML Schema any Element中所述。在这种情况下,验证器不会验证完整的 XML,跳过任何元素的内容。

于 2016-08-04T16:52:35.250 回答