jcabi-xml icon indicating copy to clipboard operation
jcabi-xml copied to clipboard

XPath is not working correctly on XML documents with schemas

Open skapral opened this issue 7 years ago • 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 avatar Feb 11 '18 16:02 skapral

@yegor256/z please, pay attention to this issue

0crat avatar Feb 11 '18 16:02 0crat

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

yegor256 avatar Feb 12 '18 15:02 yegor256

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.

borizm avatar Mar 18 '18 10:03 borizm

documentFactory.setNamespaceAware(namespaceAwareness);

@borizm is this what actually prevents using proper search through XMLs with default namespace?

longtimeago avatar Jan 24 '20 09:01 longtimeago