对于未解决的 xsi:type,Xerces-J 能否通过宽松验证是否正确?
Is Xerces-J correct to fail lax validation for an unresolved xsi:type?
我在使用这段代码(如下)验证此 SOAP Envelope
时遇到问题。
我得到的错误是:
org.xml.sax.SAXParseException; cvc-elt.4.2: Cannot resolve 'ipo:UKAddress' to a type definition for element 'shipTo'.
SOAP XSD 将 Body
定义为:
<xs:complexType name="Body">
<xs:sequence>
<xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xs:sequence>
我的期望是 "lax" 应该验证它是否有定义,但如果没有则忽略。但是,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);
}
}
简短回答:在这种情况下,Xerces 要么毫无疑问是正确的,要么不能证明是错误的。
长答案:
您没有指定您使用的是 XSD 1.0 还是 XSD 1.1。
在 1.0 中,规范对于 QName 值未解析为架构中的类型定义的 xsi:type 属性的有效性的影响略有不明确。 Validation Rule: Schema-Validity Assessment (Element) in 3.3.4 的一种自然解读是,当 xsi:type
发生时,它 必须 解决(注意在一致性要求和有效性要求之间的文本)。对该规则的另一种解读是,如果验证规则的第 1.2.1.2.3 条不适用,则显然第 1.2.1.2、1.2 和 1 条不适用,这导致得出元素应该是 laxly 的结论评估。
相同的两个读数适用于验证规则的第 4.2 节:元素局部有效(元素)在同一节中。该子句表示 xsi:type "must resolve to a type definition" 的值,这意味着如果 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 负载声明您自己的元素包装器,在其内容模型中使用跳过通配符声明它,从而强制执行您想要的验证行为。)
我在使用这段代码(如下)验证此 SOAP Envelope
时遇到问题。
我得到的错误是:
org.xml.sax.SAXParseException; cvc-elt.4.2: Cannot resolve 'ipo:UKAddress' to a type definition for element 'shipTo'.
SOAP XSD 将 Body
定义为:
<xs:complexType name="Body">
<xs:sequence>
<xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xs:sequence>
我的期望是 "lax" 应该验证它是否有定义,但如果没有则忽略。但是,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);
}
}
简短回答:在这种情况下,Xerces 要么毫无疑问是正确的,要么不能证明是错误的。
长答案:
您没有指定您使用的是 XSD 1.0 还是 XSD 1.1。
在 1.0 中,规范对于 QName 值未解析为架构中的类型定义的 xsi:type 属性的有效性的影响略有不明确。 Validation Rule: Schema-Validity Assessment (Element) in 3.3.4 的一种自然解读是,当 xsi:type
发生时,它 必须 解决(注意在一致性要求和有效性要求之间的文本)。对该规则的另一种解读是,如果验证规则的第 1.2.1.2.3 条不适用,则显然第 1.2.1.2、1.2 和 1 条不适用,这导致得出元素应该是 laxly 的结论评估。
相同的两个读数适用于验证规则的第 4.2 节:元素局部有效(元素)在同一节中。该子句表示 xsi:type "must resolve to a type definition" 的值,这意味着如果 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 负载声明您自己的元素包装器,在其内容模型中使用跳过通配符声明它,从而强制执行您想要的验证行为。)