1

我在Envelope使用这段代码(如下)验证此 SOAP 时遇到问题。

我得到的错误是:

org.xml.sax.SAXParseException;cvc-elt.4.2:无法将“ipo:UKAddress”解析为元素“shipTo”的类型定义。

SOAP XSD 定义Body为:

<xs:complexType name="Body">
<xs:sequence>
<xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xs:sequence>

我的期望是“宽松”应该验证它是否有定义,但如果没有定义则忽略。但是,对于xsi:type="ipo:UKAddress". 我只验证 SOAP Envelope- 而不是Body.

它看起来像 xerces-j 中的一个错误。在同一段代码中,XMLSchemaValidator.java :2152 实际上会在引发错误之前检查 processContents:

else if (wildcard != null && wildcard.fProcessContents == XSWildcardDecl.PC_STRICT) {

然而,XMLSchemaValidator.java :2178 没有进行此类检查,并且无论如何都会抛出。

fCurrentType = getAndCheckXsiType(element, xsiType, attributes);

对我来说,它看起来像是 xerces-j 中的一个错误。此外,Java 8 中存在此问题。感谢任何帮助或确认这确实是一个错误。

package com.example.xmlvalidate;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.CodeSource;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class Validate {
    private static final String envelope =
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
            "<soapenv:Envelope \n" +
            "  xmlns=\"http://www.w3.org/2001/XMLSchema\"" +
            "  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
            "  xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" +
            "  >\n" +
            "  <soapenv:Body>\n" +
            "    <ipo:purchaseOrder xmlns:ipo=\"http://www.example.com/IPO\">\n" +
            "      <shipTo exportCode=\"1\" xsi:type=\"ipo:UKAddress\">\n" +
            "        <name>Helen Zoe</name>\n" +
            "        <street>47 Eden Street</street>\n" +
            "        <city>Cambridge</city>\n" +
            "        <postcode>CB1 1JR</postcode>\n" +
            "      </shipTo>\n" +
            "    </ipo:purchaseOrder>\n" +
            "  </soapenv:Body>\n" +
            "</soapenv:Envelope>";

    private static final String SOAP_1_1_ENVELOPE =
            "http://schemas.xmlsoap.org/soap/envelope";
    protected static final String W3C_XML_SCHEMA =
            "http://www.w3.org/2001/XMLSchema";

    public static void validate() throws ParserConfigurationException, SAXException, IOException, TransformerException {
        final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

        documentBuilderFactory.setNamespaceAware(true);
        documentBuilderFactory.setValidating(false);

        final Class<?> clazz = documentBuilderFactory.getClass();
        final CodeSource source = clazz.getProtectionDomain().getCodeSource();
        System.out.println("Document builder implementation: " + clazz.getName() + " from : " + (source == null ? "JRE" : source));

        final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        final InputStream is = new ByteArrayInputStream(envelope.getBytes(StandardCharsets.UTF_8));
        final Document document = documentBuilder.parse(is);
        final DOMSource domSource = new DOMSource(document);

        final StreamSource streamSource = new StreamSource(new URL(SOAP_1_1_ENVELOPE).openStream());
        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final Schema schema = schemaFactory.newSchema(streamSource);

        final Validator validator = schema.newValidator();
        validator.validate(domSource);
    }
}
4

1 回答 1

1

简短的回答:在这种情况下,Xerces 要么毫无疑问是正确的,要么不可证明是错误的。

长答案:

您没有指定您使用的是 XSD 1.0 还是 XSD 1.1。

在 1.0 中,规范对 xsi:type 属性的有效性的影响稍微不清楚,该属性的 QName 值不能解析为模式中的类型定义。对3.3.4中验证规则:模式有效性评估(元素)的一种自然解读是,当xsi:type发生时,它必须解决(注意文本中一致性要求和有效性要求之间的轻微混淆)。对规则的另一种解读是,如果验证规则的第 1.2.1.2.3 条不适用,那么显然第 1.2.1.2、1.2 和 1 条不适用,从而得出该元素应该宽松的结论评估。

相同的两个读数适用于验证规则的第 4.2 条:同一部分中的元素本地有效(Element)。该子句说 xsi:type 的值“必须解析为类型定义”,这意味着如果 xsi:type 值未解析,则元素无效,或者(在规则的不同阅读中)在那个如果该元素显然不是(已知)在本地对所指示的类型有效。

在 1.1 中,规则已被改写并且可能更加清晰。如果 xsi:type 的值是不解析为类型定义的 QName,则计算回退类型,并根据回退类型验证元素;如果您想到的是,该类型将是 xsd:anyType。但是 1.1 也非常明确地表明在这种情况下 xsi:type 属性本身是无效的(验证规则的第 5 条:3.2.4中的属性本地有效。

因此,根据 XSD 1.1 的规则,很明显 Xerces 正确地将输入标记为无效,尽管错误代码可能更可能是不同的。

如果您使用 XSD 1.0 进行操作,则从错误代码中可以清楚地看出 Xerces 正在获取非解析 xsi:type 值的第一个视图,并将其视为有效性错误。我认为很难从规范文本中证明这是唯一可能的解释,但更难证明它是错误的:这显然是对规范的合理解释。如果您希望忽略 xsi:type 的问题并且不将其视为有效性错误,则需要跳过通配符,而不是宽松的通配符。(您当然可以为 SOAP 有效负载声明自己的元素包装器,在其内容模型中使用跳过通配符声明它,从而强制执行您想要的验证行为。)

于 2016-04-13T17:15:52.267 回答