【问题标题】:Java SAX XMLFilter not recognizing final variable initialized by a methodJava SAX XMLFilter 无法识别由方法初始化的最终变量
【发布时间】:2014-12-19 03:58:35
【问题描述】:

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验证真的和它有关系吗?

【问题讨论】:

  • 我发现了问题!这是一个Java错误。 XSD 代码的 JDK 开发人员在此方法中使用 == 而不是 String.equals() 将 XML 标记“childtag”与预期的 XSD“childtag”进行了比较:com.sun.org.apache.xerces.internal.impl。 xs.SubstitutionGroupHandler.getMatchingElemDecl(QName, XSElementDecl) 现在报告错误并找到解决方法...

标签: java xml validation xsd


【解决方案1】:

为了回答我自己的问题,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 返回的String 显然是不同的实例。这就是为什么== 对一个返回 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();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 2017-02-23
    • 2018-10-03
    • 1970-01-01
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多