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验证真的与它有关吗?
答案 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();