Java SAX XMLFilter无法识别由方法初始化的最终变量

时间:2014-12-19 03:58:35

标签: java xml validation xsd

Java 8. Eclipse Luna。我在分配由匿名方法体访问的最终变量时遇到了奇怪的麻烦。

背景

我正在使用我自己选择的XSD架构来验证无命名空间的XML文件。该文件没有xmlns声明,但架构有targetNamespace="mynamespace"声明。显然,该文件将无法通过验证。为了使其通过验证,我给验证器提供了一个过滤器,强制它假装XML文件具有xmlns="mynamespace",如建议here。请参阅下面的代码。

问题

注意变量targetNamespace。如果我使用字符串文字命名空间初始化它,则验证通过。如果我使用返回相同值的方法调用进行初始化,则验证失败。

守则

public static void validate(byte[] xmlFile, byte[] xsdFile)
        throws SAXException, IOException, XMLStreamException, FactoryConfigurationError {

    final String targetNamespace = "mynamespace"; // PASS!
    //final String targetNamespace = getTargetNamespace(xsdFile); // FAIL!

    XMLFilter namespaceFilter = new XMLFilterImpl(XMLReaderFactory.createXMLReader()) {
        @Override
        public void startElement(String uri, String localName, String qName, Attributes atts)
                throws SAXException {
            // Make the validator think the XML file's elements have a namespace
            uri = targetNamespace;

            super.startElement(uri, localName, qName, atts);
        }
    };

    Source xmlSource = new SAXSource(namespaceFilter, new InputSource(new ByteArrayInputStream(xmlFile)));
    Source xsdSource = new StreamSource(new ByteArrayInputStream(xsdFile));
    SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(xsdSource).newValidator().validate(xmlSource);
}

private static String getTargetNamespace(byte[] xsdFile)
        throws XMLStreamException, FactoryConfigurationError {
    XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(new ByteArrayInputStream(xsdFile));
    while (reader.hasNext()) {
        int event = reader.next();

        // Get the root element's "targetNamespace" attribute
        if (event == XMLEvent.START_ELEMENT) {
            return reader.getAttributeValue(null, "targetNamespace");
        }
    }
    return null;
}

错误

  

cvc-complex-type.2.4.a:从元素'childtag'开始发现无效内容。其中一个'{“mynamespace”:childtag}'是预期的。

变得更奇怪。当方法返回"mynamespace"作为字符串文字时,验证通过。但是,当方法从XSD文件获取"mynamespace"的值时,验证失败。我已经使用String.equals()验证了验证失败时传递给super.startElement的值确实与验证通过时完全相同

我在这里看到的唯一区别是,当分配给最终变量targetNamespace的值最终为常量(字符串文字)时,它会通过,并且当值变为失败时分配最终由某些条件确定(从XSD文件派生)。最终或有效的决赛没有任何区别。

这是一个已知的Java错误吗?或者是我对从匿名方法访问的变量的初始化缺乏了解? XSD验证真的与它有关吗?

1 个答案:

答案 0 :(得分:0)

要回答我自己的问题,似乎JDK 8.25在此代码中有一个错误:

com.sun.org.apache.xerces.internal.impl.xs.SubstitutionGroupHandler:

public XSElementDecl getMatchingElemDecl(QName element, XSElementDecl exemplar) {
    if (element.localpart == exemplar.fName &&
        element.uri == exemplar.fTargetNamespace) {
        return exemplar;
    }
    ...
}

==(参考比较)而不是equals方法(内容比较)进行比较。虽然字符串文字"mynamespace"看起来与exemplar.fTargetNamespace存储的内存中的String对象相同,但reader.getAttributeValue返回的字符串显然是一个不同的实例。这就是为什么==为一个返回true,为另一个返回false。

// Pass because same instance in memory
String targetNamespace = "mynamespace";

// Fail because different instance in memory
String targetNamespace = reader.getAttributeValue(null, "targetNamespace");

您无法更改JDK代码,因此解决方法是在变量上调用String.intern(),并且您有更高的机会抵御==比较等错误。

targetNamespace = targetNamespace.intern();