Правильно ли Xerces-J провалить слабую проверку для неразрешенного xsi:type?

У меня проблема с проверкой этого SOAP Envelope используя этот фрагмент кода (ниже).

Я получаю ошибку:

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

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 Правил валидации: Элемент локально действителен (Элемент) в том же разделе. В этом предложении говорится, что значение xsi:type "должно разрешаться до определения типа", что означает либо недопустимость элемента, если значение xsi:type не разрешается, либо (при другом чтении правила), что в этом case элемент явно (как известно, не является) локально действительным по отношению к указанному типу.

В версии 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, объявив ее с помощью символа пропуска в своей модели содержимого и, таким образом, принудительно запустить желаемое поведение проверки.)

Другие вопросы по тегам