XPath is not working correctly on XML documents with schemas
skapral opened this issue · 4 comments
Assume the following test suite:
public class ProbaTest {
@Test
public void test() throws Exception {
new XMLDocument(
ProbaTest.class.getClassLoader().getResourceAsStream("pom.xml")
).xpath("//project/scm/url/text()").get(0);
}
@Test
public void test2() throws Exception {
new StrictXML(
new XMLDocument(
ProbaTest.class.getClassLoader().getResourceAsStream("pom.xml")
)
).xpath("//project/scm/url/text()").get(0);
}
@Test
public void test3() throws Exception {
new XMLDocument(
ProbaTest.class.getClassLoader().getResourceAsStream("pom_wo_schemas.xml")
).xpath("//project/scm/url/text()").get(0);
}
}
...where pom.xml
is the POM file, taken from Takes framework (link) and pom_wo_schemas.xml
is the same POM file with all attributes from root project
element removed.
First two tests fail with similar exception:
com.jcabi.xml.ListWrapper$NodeNotFoundException: XPath '//project/scm/url/text()' not found in '<?xml version="1.0" encoding="UTF-8" standalone="n..13243../profile>\uA </profiles>\uA</project>\uA': Index (0) is out of bounds (size=0)
at com.jcabi.xml.ListWrapper.get(ListWrapper.java:162)
at proba.ProbaTest.test2(ProbaTest.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Third successfully passes.
It seems that either jcabi xml is not workable on XML documents with schemas referenced in root node, or it misses the necessary documentation.
Complete test suite can be found here
@skapral here is what you need:
new XMLDocument(ProbaTest.class.getClassLoader().getResourceAsStream("pom.xml"))
.registerNs("m", "http://maven.apache.org/POM/4.0.0")
.xpath("//m:project/m:scm/m:url/text()")
.get(0);
XMLDocument should have the initialization of the factory delayed and all the constructors should be private or protected, and the builder pattern should be used.
This is dictated by the need of delaying of the setting:
documentFactory.setNamespaceAware(namespaceAwareness);
to true, base on the fact registerNs() was invoked or not.
It could look like that:
XMLDocument.of()
.namespaces().put("m", "http://maven.apache.org/POM/4.0.0")
.from(ProbaTest.class.getClassLoader().getResourceAsStream("pom.xml"))
.build()
.xpath("//m:project/m:scm/m:url/text()")
.get(0);
or (without namespaces):
XMLDocument.of()
.from(ProbaTest.class.getClassLoader().getResourceAsStream("pom.xml"))
.build()
.xpath("//project/scm/url/text()")
.get(0);
Of course it will break the "contract" with all the existing code that uses this class.
documentFactory.setNamespaceAware(namespaceAwareness);
@borizm is this what actually prevents using proper search through XMLs with default namespace?