当前位置: 首页 > 知识库问答 >
问题:

如何在Android上将Java XPath与KML文件和名称空间结合使用

董洲
2023-03-14

我正在为如何在包含新的gx: Track和gx: coord标签的KML文件上使用XPath而苦苦挣扎。问题是如何在Android下的命名空间中使用XPath。

我看了很多例子,包括

>

https://howtodoinjava.com/xml/xpath-namespace-resolution-example/

Java中带名称空间的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() ) );
}

test()方法执行3条XPath语句/模式,并为两个测试文档中的每一个调用一次。这两个文件使用不同的方法构建,但内容应该相同。然而,我从3条XPath语句中得到的结果是不同的。

以下是文件1的结果:

2018-11-17 17:51:28.289 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.324 22837-22837/ca.csdesigninc.offroadtracker D/TEST-RESULT1: 1
2018-11-17 17:51:28.334 22837-22837/ca.csdesigninc.offroadtracker D/TEST-RESULT2: 1
2018-11-17 17:51:28.343 22837-22837/ca.csdesigninc.offroadtracker D/TEST-RESULT3: 0

以下是文件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

至少有两个问题:

>

为什么第三条XPath语句在文档1和文档2中都找不到gx:Track元素?

更新:这个问题似乎与

xmlns="http://www.opengis.net/kml/2.2"

包含在文件2中。如果我删除它,前两个XPath测试的结果是正确的(对于这两个文档),事实上XPath测试3现在可以在文档2上运行。不幸的是,我仍然无法控制这种行为。

我可能错过了一些明显的东西,如果有任何帮助,我将不胜感激。

共有1个答案

黄鸣
2023-03-14

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

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

当您构造kml元素时,使用createElement()创建一个绑定到"no命名空间"的元素。然后,您添加了命名空间属性,这些属性在使用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)));

然而,这是作弊。你真正想做的是首先确保元素被正确创建。创建要绑定到命名空间的元素时,请使用createElements()方法,如createElement()的JavaDoc注释所示:

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

因此,要创建一个绑定到http://www.opengis.net/kml/2.2namespace,您需要使用:

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

和:

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

这同样适用于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命名空间,所以它不会选择它们。

在重写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()));

}
 类似资料:
  • 我对C++不是很熟悉,这是我第一次使用的不仅仅是名称空间std。下面的有什么区别? VS 在这两个之后,我现在似乎可以创建一个MyClass对象了。一种方式比另一种好吗?另外,如果我都不这样做,我还可以通过每次需要时在MyNamespace::MyClass之前附加MyClass来引用MyClass吗? 接下来,如果我使用第二个选项转发declare,我还需要吗?在我(不是很好)的理解中,在C++

  • 问题内容: 我在命名空间和语句上遇到了一些麻烦。 我有三个文件:,和。 我正在尝试使用相对路径进行此操作,因此已将其放在所有类中: 在我的圈子课程中,我有以下内容: 如果使用这些语句,我不会出错。如果我尝试以下语句,则会得到: 致命错误:在第8行的/Users/shawn/Documents/work/sites/workspace/shape/Circle.php中找不到类’Shape \ Sh

  • 我希望在Atom中有一种替代Sublime文本包Php伴侣的方法,允许导入名称空间和类。 我试图弄清楚atom autocomplete php插件中的这个功能是如何工作的。 文档中列出了以下功能: 自动完成类名,并在需要时自动添加use语句 在游标下添加类的use语句(ctrl-alt-u) 所以当我需要在我的PHP文件中导入一个类时,我会使用 我会得到一系列选项,但我的列表中没有列出我需要的c

  • 问题内容: 我有一个像这样开始的XML文件: 我将不得不打开许多这些文件。它们中的每一个都有不同的名称空间,但一次只能有一个名称空间(我永远不会在一个xml文件中找到两个定义的名称空间)。 使用XPath,我希望有一种自动的方法将给定的名称空间添加到名称空间管理器中。到目前为止,我只能通过解析xml文件来获取名称空间,但是我有一个XPathNavigator实例,并且它应该具有一种不错且干净的方式

  • 问题内容: 我想操作具有默认名称空间但没有前缀的xml文档。有没有办法使用没有名称空间uri的xpath,就像没有名称空间一样? 我相信,如果将documentBuilderFactory的namespaceAware属性设置为false,那应该是可能的。但就我而言,它不起作用。 我的理解是不正确的还是我在代码中做错了? 这是我的代码: 这是我的xml: 问题答案: 使用默认名称空间(无前缀)的文

  • 问题内容: 我有以下格式的xml文档: 我需要使用lxml中的xpath检索所有元素。我的问题是我不知道如何使用空的名称空间。我尝试了以下示例,但没有用。请指教。 我尝试过的各种方法是: 要么 要么 在这一点上,我只是不知道该尝试什么。任何帮助是极大的赞赏。 问题答案: 这样的事情应该起作用: 另请参见http://lxml.de/xpathxslt.html#namespaces-and- pr