如何在Android上将JavaXPath与KML文件和名称空间一起使用

如何在Android上将JavaXPath与KML文件和名称空间一起使用,java,android,xpath,xml-namespaces,Java,Android,Xpath,Xml Namespaces,我正在努力研究如何在包含新的gx:Track和gx:coord标记的KML文件上使用XPath。问题在于如何在Android下使用XPath和名称空间 我看了很多例子,包括 但我似乎连这些例子都不起作用 以下代码和输出说明了我的问题: public App() { super(); try { test( testDoc1() ); test( testDoc2() ); } catch( Exception e ) {

我正在努力研究如何在包含新的gx:Track和gx:coord标记的KML文件上使用XPath。问题在于如何在Android下使用XPath和名称空间

我看了很多例子,包括

但我似乎连这些例子都不起作用

以下代码和输出说明了我的问题:

public App() {
    super();
    try {
        test( testDoc1() );
        test( testDoc2() );
    } catch( Exception e ) {
        e.printStackTrace();
    } finally {
        Log.d( "TEST-FINISHED", "test is finished" );
    }
}

private String toXmlString( Document document ) throws TransformerException {
    DOMSource domSource = new DOMSource( document );
    StringWriter writer = new StringWriter();
    StreamResult result = new StreamResult( writer );
    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer transformer = tf.newTransformer();
    transformer.transform( domSource, result );
    return writer.toString();
}

private Document testDoc1() throws ParserConfigurationException {
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    documentBuilderFactory.setNamespaceAware( true );
    Document mDocument = documentBuilderFactory.newDocumentBuilder().newDocument();

    String XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
    Element mKmlElement = mDocument.createElement( "kml" );
    mKmlElement.setAttributeNS( XMLNS_NAMESPACE_URI, "xmlns", "http://www.opengis.net/kml/2.2" );
    mKmlElement.setAttributeNS( XMLNS_NAMESPACE_URI, "xmlns:gx", "http://www.google.com/kml/ext/2.2" );
    mDocument.appendChild( mKmlElement );

    Element mPlacemarkElement = mDocument.createElement( "Placemark" );
    mKmlElement.appendChild( mPlacemarkElement );

    Element gxTrackElement = mDocument.createElement( "gx:Track" );
    mPlacemarkElement.appendChild( gxTrackElement );

    Element gxCoordElement = mDocument.createElement( "gx:coord" );
    gxCoordElement.setTextContent( "-122.207881 37.371915 156.000000" );
    gxTrackElement.appendChild( gxCoordElement );

    return mDocument;
}

private Document testDoc2() throws ParserConfigurationException, IOException, SAXException {
    String kmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark><gx:Track><gx:coord>-122.207881 37.371915 156.000000</gx:coord></gx:Track></Placemark></kml>";

    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    documentBuilderFactory.setNamespaceAware( true );
    Document mDocument = documentBuilderFactory.newDocumentBuilder().parse( new InputSource( new StringReader( kmlString ) ) );

    return mDocument;
}

private void test( Document mDocument ) throws Exception {
    String xml = toXmlString( mDocument );
    Log.d( "TEST-XML", xml );

    XPath xPath = XPathFactory.newInstance().newXPath();
    xPath.setNamespaceContext( new NamespaceContext() {
        @Override
        public String getNamespaceURI( String prefix ) {
            switch( prefix ) {
                case XMLConstants.DEFAULT_NS_PREFIX:
                    return "http://www.opengis.net/kml/2.2";
                case "gx":
                    return "http://www.google.com/kml/ext/2.2";
            }
            return XMLConstants.NULL_NS_URI;
        }

        @Override
        public String getPrefix( String namespaceURI ) {
            return null;
        }

        @Override
        public Iterator getPrefixes( String namespaceURI ) {
            return null;
        }
    } );
    NodeList result1 = (NodeList) xPath.evaluate( "/kml", mDocument, XPathConstants.NODESET );
    Log.d( "TEST-RESULT1", String.valueOf( result1.getLength() ) );
    NodeList result2 = (NodeList) xPath.evaluate( "/kml/Placemark", mDocument, XPathConstants.NODESET );
    Log.d( "TEST-RESULT2", String.valueOf( result2.getLength() ) );
    NodeList result3 = (NodeList) xPath.evaluate( "/kml/Placemark/gx:Track", mDocument, XPathConstants.NODESET );
    Log.d( "TEST-RESULT3", String.valueOf( result3.getLength() ) );
}
以下是文件2的结果:

2018-11-17 17:51:28.348 22837-22837/ca.csdesigninc.offroadtracker D/TEST-XML: <?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2"><Placemark><gx:Track><gx:coord>-122.207881 37.371915 156.000000</gx:coord></gx:Track></Placemark></kml> 2018-11-17 17:51:28.358 22837-22837/ca.csdesigninc.offroadtracker D/TEST-RESULT1: 0 2018-11-17 17:51:28.363 22837-22837/ca.csdesigninc.offroadtracker D/TEST-RESULT2: 0 2018-11-17 17:51:28.372 22837-22837/ca.csdesigninc.offroadtracker D/TEST-RESULT3: 0 包括在文件2中。如果我删除它,前2个XPath测试的结果是正确的(对于这两个文档),事实上XPath测试3现在可以在文档2上运行。不幸的是,我仍然无法控制这种行为


我可能遗漏了一些明显的东西,希望您能提供帮助。

这些差异是由于名称空间造成的。无论是XML的生成方式,还是在XPath中选择内容的时间

不幸的是,很难看出区别,因为碰巧由
testDoc1()
toXmlString()
序列化的XML与内存中文档的状态不完全匹配

当您使用
createElement()
构造
kml
元素时,它会创建一个绑定到“无命名空间”的元素。然后,添加了名称空间属性,当使用
toXmlString()
进行序列化时,这些属性会出现,并使
kml
元素出现在
http://www.opengis.net/kml/2.2
名称空间

如果要将该XML封送回新的
文档
对象,则
kml
元素将绑定到该名称空间。但是,该元素的当前内存中对象不是

您可以通过添加一些附加诊断信息
println
来观察这一点:

NodeList result1 = (NodeList) xPath.evaluate("/kml", mDocument, XPathConstants.NODESET);
System.out.println(String.valueOf(result1.getLength()));
System.out.println("Namespace URI: " + result1.item(0).getNamespaceURI());
System.out.println("Prefix: " + result1.item(0).getPrefix());
您可以对XML进行往返,并观察到在整理序列化的XML时,它的行为有所不同:

private void test(Document mDocument) throws Exception {
  String xml = toXmlString(mDocument);
  System.out.println( xml);
  DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  documentBuilderFactory.setNamespaceAware(true);
  mDocument = documentBuilderFactory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
然而,这是欺骗。您真正想要做的是首先确保正确创建元素。创建要绑定到命名空间的元素时,请使用
createElementNS()
方法,如JavaDoc注释中所示:

要创建具有限定名称和命名空间URI的元素,请使用
createElementNS
方法

因此,要创建绑定到
http://www.opengis.net/kml/2.2
命名空间,您可能希望使用:

Element mKmlElement = mDocument.createElementNS("http://www.opengis.net/kml/2.2", "kml"); 
以及:

对于
gx:Track
元素也是如此:

Element gxTrackElement = mDocument.createElementNS("http://www.google.com/kml/ext/2.2","gx:Track");
一旦让文档对象真正相等和正确,就需要调整XPath

对于XPath,如果不应用名称空间前缀,它将选择绑定到“无名称空间”的元素。因此,
/kml
将只选择未绑定到命名空间的
kml
元素。但是由于
kml
元素绑定到
http://www.opengis.net/kml/2.2
namespace,它不会选择它们

在重写
getNamespaceURI()
函数时,可以为Google KML扩展名称空间保留
gx
,然后将任何其他名称空间前缀默认解析为
http://www.opengis.net/kml/2.2

@Override
public String getNamespaceURI(String prefix) {
  return "gx".equals(prefix) ? "http://www.google.com/kml/ext/2.2" : "http://www.opengis.net/kml/2.2";
}
然后,调整XPath语句,为这些KML元素使用前缀。如果您使用上述代码,那么您使用的前缀并不重要。除gx以外的任何内容都将返回
http://www.opengis.net/kml/2.2
名称空间

NodeList result1 = (NodeList) xPath.evaluate("/k:kml", mDocument, XPathConstants.NODESET);
System.out.println(String.valueOf(result1.getLength()));
System.out.println("Namespace URI: " + result1.item(0).getNamespaceURI());
System.out.println("Prefix: " + result1.item(0).getPrefix());

NodeList result2 = (NodeList) xPath.evaluate("/k:kml/k:Placemark", mDocument, XPathConstants.NODESET);
System.out.println( String.valueOf(result2.getLength()));
NodeList result3 = (NodeList) xPath.evaluate("/k:kml/k:Placemark/gx:Track", mDocument, XPathConstants.NODESET);
System.out.println(String.valueOf(result3.getLength()));
总而言之:

public App() {
  super();
  try {
    test( testDoc1() );
    test( testDoc2() );
  } catch( Exception e ) {
    e.printStackTrace();
  } finally {
    Log.d( "TEST-FINISHED", "test is finished" );
  }
}
private String toXmlString(Document document) throws TransformerException {
  DOMSource domSource = new DOMSource(document);
  StringWriter writer = new StringWriter();
  StreamResult result = new StreamResult(writer);
  TransformerFactory tf = TransformerFactory.newInstance();
  Transformer transformer = tf.newTransformer();
  transformer.transform(domSource, result);
  return writer.toString();
}

private Document testDoc1() throws ParserConfigurationException {
  DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  documentBuilderFactory.setNamespaceAware(true);
  Document mDocument = documentBuilderFactory.newDocumentBuilder().newDocument();

  String XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
  //Element mKmlElement = mDocument.createElement("kml");
  Element mKmlElement = mDocument.createElementNS("http://www.opengis.net/kml/2.2", "kml");
  //mKmlElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns", "http://www.opengis.net/kml/2.2");
  mKmlElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:gx", "http://www.google.com/kml/ext/2.2");
  mDocument.appendChild(mKmlElement);


  //Element mPlacemarkElement = mDocument.createElement("Placemark");
  Element mPlacemarkElement = mDocument.createElementNS("http://www.opengis.net/kml/2.2", "Placemark");
  //mPlacemarkElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns", "http://www.opengis.net/kml/2.2");
  mKmlElement.appendChild(mPlacemarkElement);

  //Element gxTrackElement = mDocument.createElement("gx:Track");
  Element gxTrackElement = mDocument.createElementNS("http://www.google.com/kml/ext/2.2","gx:Track");
  mPlacemarkElement.appendChild(gxTrackElement);

  //Element gxCoordElement = mDocument.createElement("gx:coord");
  Element gxCoordElement = mDocument.createElementNS("http://www.google.com/kml/ext/2.2", "gx:coord");
  gxCoordElement.setTextContent("-122.207881 37.371915 156.000000");
  gxTrackElement.appendChild(gxCoordElement);

  return mDocument;
}

private Document testDoc2() throws ParserConfigurationException, IOException, SAXException {
  String kmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark><gx:Track><gx:coord>-122.207881 37.371915 156.000000</gx:coord></gx:Track></Placemark></kml>";

  DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  documentBuilderFactory.setNamespaceAware(true);
  Document mDocument = documentBuilderFactory.newDocumentBuilder().parse(new 
  InputSource(new StringReader(kmlString)));

  return mDocument;
}

private void test(Document mDocument) throws Exception {
  String xml = toXmlString(mDocument);
  System.out.println( xml);

  XPath xPath = XPathFactory.newInstance().newXPath();

  xPath.setNamespaceContext(new NamespaceContext() {
    @Override
    public String getNamespaceURI(String prefix) {
      return "gx".equals(prefix) ? "http://www.google.com/kml/ext/2.2" : "http://www.opengis.net/kml/2.2";
    }

    @Override
    public String getPrefix(String namespaceURI) {
      if ("http://www.google.com/kml/ext/2.2".equals(namespaceURI)) {
        return "gx";
      }
      return null;
    }

    @Override
    public Iterator getPrefixes(String namespaceURI) {
      List<String> ns = new ArrayList<>();
      ns.add("gx");
      return ns.iterator();
    }
  });

  NodeList result1 = (NodeList) xPath.evaluate("/k:kml", mDocument, XPathConstants.NODESET);
  System.out.println(String.valueOf(result1.getLength()));
  System.out.println("Namespace URI: " + result1.item(0).getNamespaceURI());
  System.out.println("Prefix: " + result1.item(0).getPrefix());

  NodeList result2 = (NodeList) xPath.evaluate("/k:kml/k:Placemark", mDocument, XPathConstants.NODESET);
  System.out.println( String.valueOf(result2.getLength()));
  NodeList result3 = (NodeList) xPath.evaluate("/k:kml/k:Placemark/gx:Track", mDocument, XPathConstants.NODESET);
  System.out.println(String.valueOf(result3.getLength()));

}
public应用程序(){
超级();
试一试{
test(testDoc1());
测试(testDoc2());
}捕获(例外e){
e、 printStackTrace();
}最后{
Log.d(“测试完成”、“测试完成”);
}
}
私有字符串toXmlString(文档文档)引发TransformerException{
DOMSource DOMSource=新的DOMSource(文档);
StringWriter编写器=新的StringWriter();
StreamResult结果=新的StreamResult(writer);
TransformerFactory tf=TransformerFactory.newInstance();
变压器=tf.新变压器();
transformer.transform(domSource,result);
返回writer.toString();
}
私有文档testDoc1()引发ParserConfiguration异常{
DocumentBuilderFactory DocumentBuilderFactory=DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
Document mDocument=documentBuilderFactory.newDocumentBuilder().newDocument();
字符串XMLNS_名称空间_URI=”http://www.w3.org/2000/xmlns/";
//元素mKmlElement=mDocument.createElement(“kml”);
元素mKmlElement=mDocument.createElements(“http://www.opengis.net/kml/2.2“,“kml”);
//setAttributeNS(XMLNS_名称空间_URI,“XMLNS”,“http://www.opengis.net/kml/2.2");
setAttributeNS(XMLNS_名称空间_URI,“XMLNS:gx,”http://www.google.com/kml/ext/2.2");
mDocument.appendChild(mKmlElement);
//Element-mPlacemarkElement=mDocument.createElement(“Placemark”);
Element-mPlacemarkElement=mDocument.createElements(“http://www.opengis.net/kml/2.2“,”地标“);
//setAttributeNS(XMLNS_名称空间_URI,“XMLNS”,“http://www.opengis.net/kml/2.2");
appendChild(mPlacemarkElement);
//元素gxTrackElement=mDocument.createElement(“gx:Track”);
元素gxTrackElement=mDocument.createElements(“http://www.google.com/kml/ext/2.2“,“gx:轨道”);
appendChild(gxTrackElement);
//元素gxCoordElement=mDocument.createElement(“gx:coord”);
元素gxCoordElement=mDocument.createElements(“http://www.google.com/kml/ext/2.2“,“gx:coord”);
gxCoordElement.setTextContent(“-122.207881 37.371915 156.000000”);
追加子元素(gxCoordElement);
返回mDocument;
}
私有文档testDoc2()抛出ParserConfiguration异常、IOException、SAXException{
字符串kmlString=“-122.207881 37.371915 156.000000”;
DocumentBuilderFactory documentBuilderFactor
@Override
public String getNamespaceURI(String prefix) {
  return "gx".equals(prefix) ? "http://www.google.com/kml/ext/2.2" : "http://www.opengis.net/kml/2.2";
}
NodeList result1 = (NodeList) xPath.evaluate("/k:kml", mDocument, XPathConstants.NODESET);
System.out.println(String.valueOf(result1.getLength()));
System.out.println("Namespace URI: " + result1.item(0).getNamespaceURI());
System.out.println("Prefix: " + result1.item(0).getPrefix());

NodeList result2 = (NodeList) xPath.evaluate("/k:kml/k:Placemark", mDocument, XPathConstants.NODESET);
System.out.println( String.valueOf(result2.getLength()));
NodeList result3 = (NodeList) xPath.evaluate("/k:kml/k:Placemark/gx:Track", mDocument, XPathConstants.NODESET);
System.out.println(String.valueOf(result3.getLength()));
public App() {
  super();
  try {
    test( testDoc1() );
    test( testDoc2() );
  } catch( Exception e ) {
    e.printStackTrace();
  } finally {
    Log.d( "TEST-FINISHED", "test is finished" );
  }
}
private String toXmlString(Document document) throws TransformerException {
  DOMSource domSource = new DOMSource(document);
  StringWriter writer = new StringWriter();
  StreamResult result = new StreamResult(writer);
  TransformerFactory tf = TransformerFactory.newInstance();
  Transformer transformer = tf.newTransformer();
  transformer.transform(domSource, result);
  return writer.toString();
}

private Document testDoc1() throws ParserConfigurationException {
  DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  documentBuilderFactory.setNamespaceAware(true);
  Document mDocument = documentBuilderFactory.newDocumentBuilder().newDocument();

  String XMLNS_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
  //Element mKmlElement = mDocument.createElement("kml");
  Element mKmlElement = mDocument.createElementNS("http://www.opengis.net/kml/2.2", "kml");
  //mKmlElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns", "http://www.opengis.net/kml/2.2");
  mKmlElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:gx", "http://www.google.com/kml/ext/2.2");
  mDocument.appendChild(mKmlElement);


  //Element mPlacemarkElement = mDocument.createElement("Placemark");
  Element mPlacemarkElement = mDocument.createElementNS("http://www.opengis.net/kml/2.2", "Placemark");
  //mPlacemarkElement.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns", "http://www.opengis.net/kml/2.2");
  mKmlElement.appendChild(mPlacemarkElement);

  //Element gxTrackElement = mDocument.createElement("gx:Track");
  Element gxTrackElement = mDocument.createElementNS("http://www.google.com/kml/ext/2.2","gx:Track");
  mPlacemarkElement.appendChild(gxTrackElement);

  //Element gxCoordElement = mDocument.createElement("gx:coord");
  Element gxCoordElement = mDocument.createElementNS("http://www.google.com/kml/ext/2.2", "gx:coord");
  gxCoordElement.setTextContent("-122.207881 37.371915 156.000000");
  gxTrackElement.appendChild(gxCoordElement);

  return mDocument;
}

private Document testDoc2() throws ParserConfigurationException, IOException, SAXException {
  String kmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark><gx:Track><gx:coord>-122.207881 37.371915 156.000000</gx:coord></gx:Track></Placemark></kml>";

  DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
  documentBuilderFactory.setNamespaceAware(true);
  Document mDocument = documentBuilderFactory.newDocumentBuilder().parse(new 
  InputSource(new StringReader(kmlString)));

  return mDocument;
}

private void test(Document mDocument) throws Exception {
  String xml = toXmlString(mDocument);
  System.out.println( xml);

  XPath xPath = XPathFactory.newInstance().newXPath();

  xPath.setNamespaceContext(new NamespaceContext() {
    @Override
    public String getNamespaceURI(String prefix) {
      return "gx".equals(prefix) ? "http://www.google.com/kml/ext/2.2" : "http://www.opengis.net/kml/2.2";
    }

    @Override
    public String getPrefix(String namespaceURI) {
      if ("http://www.google.com/kml/ext/2.2".equals(namespaceURI)) {
        return "gx";
      }
      return null;
    }

    @Override
    public Iterator getPrefixes(String namespaceURI) {
      List<String> ns = new ArrayList<>();
      ns.add("gx");
      return ns.iterator();
    }
  });

  NodeList result1 = (NodeList) xPath.evaluate("/k:kml", mDocument, XPathConstants.NODESET);
  System.out.println(String.valueOf(result1.getLength()));
  System.out.println("Namespace URI: " + result1.item(0).getNamespaceURI());
  System.out.println("Prefix: " + result1.item(0).getPrefix());

  NodeList result2 = (NodeList) xPath.evaluate("/k:kml/k:Placemark", mDocument, XPathConstants.NODESET);
  System.out.println( String.valueOf(result2.getLength()));
  NodeList result3 = (NodeList) xPath.evaluate("/k:kml/k:Placemark/gx:Track", mDocument, XPathConstants.NODESET);
  System.out.println(String.valueOf(result3.getLength()));

}