4

我正在尝试从 Google 的地理编码 API 中解析出一些信息,但是在有效地从 xml 中获取数据时遇到了一些麻烦。例如见链接

我真正关心的是获取类型所在的位置和short_name来自的位置, 但是使用我的测试程序,我的 XPath 查询对这两个查询都没有返回任何结果。address_componentadministrative_area_level_1long_nameadministrative_area_level_2

public static void Main(string[] args)
{
    using(WebClient webclient = new WebClient())
    {
        webclient.Proxy = null;
        string locationXml = webclient.DownloadString("http://maps.google.com/maps/api/geocode/xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false");
        using(var reader = new StringReader(locationXml))
        {
            var doc = new XPathDocument(reader);
            var nav = doc.CreateNavigator();
            Console.WriteLine(nav.SelectSingleNode("/GeocodeResponse/result/address_component[type=administrative_area_level_1]/short_name").InnerXml);
            Console.WriteLine(nav.SelectSingleNode("/GeocodeResponse/result/address_component[type=administrative_area_level_2]/long_name").InnerXml);

        }
    }
}

谁能帮我找出我做错了什么,或者推荐一个更好的方法?

4

5 回答 5

4

您需要将要查找的节点的值放在引号中:

".../address_component[type='administrative_area_level_1']/short_name"
                            ↑                           ↑
于 2010-08-18T19:17:37.917 回答
4

我绝对推荐使用 LINQ to XML 而不是 XPathNavigator。根据我的经验,它使 XML 查询变得轻而易举。在这种情况下,我不确定到底出了什么问题……但我会想出一个 LINQ to XML 片段来代替。

using System;
using System.Linq;
using System.Net;
using System.Xml.Linq;

class Test
{
    public static void Main(string[] args)
    {
        using(WebClient webclient = new WebClient())
        {
            webclient.Proxy = null;
            string locationXml = webclient.DownloadString
                ("http://maps.google.com/maps/api/geocode/xml?address=1600"
                 + "+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false");
            XElement root = XElement.Parse(locationXml);

            XElement result = root.Element("result");
            Console.WriteLine(result.Elements("address_component")
                                    .Where(x => (string) x.Element("type") ==
                                           "administrative_area_level_1")
                                    .Select(x => x.Element("short_name").Value)
                                    .First());
            Console.WriteLine(result.Elements("address_component")
                                    .Where(x => (string) x.Element("type") ==
                                           "administrative_area_level_2")
                                    .Select(x => x.Element("long_name").Value)
                                    .First());
        }
    }
}

现在这更多代码1 ...但我个人发现它比 XPath 更容易正确,因为编译器对我的帮助更大。

编辑:我觉得值得更详细地说明为什么我通常更喜欢这样的代码而不是使用 XPath,即使它显然更长。

当您在 C# 程序中使用 XPath 时,您有两种不同的语言 - 但只有一种语言是可控的 (C#)。XPath 被归入字符串领域:Visual Studio 没有对 XPath 表达式进行任何特殊处理;它不明白它是一个 XPath 表达式,所以它不能帮助你。并不是 Visual Studio 不了解 XPath,而是不知道 XPath。正如 Dimitre 指出的那样,如果您正在编辑 XSLT 文件,而不是 C# 文件,它完全能够发现错误。

每当您将一种语言嵌入另一种语言并且该工具不知道它时,就会出现这种情况。常见的例子是:

  • SQL
  • 常用表达
  • HTML
  • XPath

当代码在另一种语言中以数据形式呈现时,第二语言会失去很多工具优势。

虽然您可以在所有地方进行上下文切换,将 XPath(或 SQL,或正则表达式等)提取到他们自己的工具中(可能在同一个实际程序中,但在单独的文件或窗口中),但我发现这更难 -从长远来看要阅读代码。如果只写过代码,之后再也不读,那可能没问题 - 但你确实需要能够在之后阅读代码,我个人认为发生这种情况时可读性会受到影响。

上面的 LINQ to XML 版本只对纯数据使用字符串——元素的名称等——并使用代码(方法调用)来表示诸如“查找具有给定名称的元素”或“应用此过滤器”之类的操作。在我看来,这是更惯用的 C# 代码。

显然其他人不同意这个观点,但我认为值得扩展以显示我来自哪里。

请注意,这当然不是硬性规定……在某些情况下,XPath、正则表达式等是最好的解决方案。在这种情况下,我更喜欢 LINQ to XML,仅此而已。


1当然,我可以将每个Console.WriteLine调用保留在一行,但我不喜欢在 SO 上发布带有水平滚动条的代码。请注意,使用与上述相同的缩进编写正确的 XPath 版本并避免滚动仍然非常讨厌:

            Console.WriteLine(nav.SelectSingleNode("/GeocodeResponse/result/" +
                "address_component[type='administrative_area_level_1']" +
                "/short_name").InnerXml);

一般来说,长行在 Visual Studio 中比在 Stack Overflow 上的效果要好得多……

于 2010-08-18T19:18:25.967 回答
2

我建议只在 Visual Studio 中输入 XPath 表达式作为 XSLT 文件的一部分。您将在“键入时”收到错误消息——这是一个出色的 XML/XSLT/XPath 编辑器。

例如,我正在输入:

<xsl:apply-templates select="@* | node() x"/>

并立即在错误列表窗口中出现以下错误

Error   9   Expected end of the expression, found 'x'.  @* | node()  -->x<--

XSLTFile1.xslt  9   14  Miscellaneous Files

只有当 XPath 表达式没有引发任何错误时(我也可能会测试它是否也选择了预期的节点),我才会将此表达式放入我的 C# 代码中。

这确保了我在运行 C# 程序时不会出现 XPath(语法和语义)错误。

于 2010-08-19T20:19:59.410 回答
1

dtb 的回答是准确的。我想补充一点,您可以使用以下链接之类的 xpath 测试工具来帮助找到正确的 xpath:

http://www.bit-101.com/xpath/

于 2010-08-18T19:21:58.643 回答
0
string url = @"http://maps.google.com/maps/api/geocode/xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false";
string value = "administrative_area_level_1";

using(WebClient client = new WebClient())
{
    string wcResult = client.DownloadString(url);

    XDocument xDoc = XDocument.Parse(wcResult);

    var result = xDoc.Descendants("address_component")
                    .Where(p=>p.Descendants("type")
                                .Any(q=>q.Value.Contains(value))
                    );

}

结果是“address_component”的枚举,其中至少有一个“类型”节点包含您正在搜索的值。上面查询的结果是一个包含以下数据的 XElement。

<address_component>
  <long_name>California</long_name>
  <short_name>CA</short_name>
  <type>administrative_area_level_1</type>
  <type>political</type>
</address_component> 

我真的建议花一点时间学习 LINQ,因为它对于操作和查询内存中的对象、查询数据库非常有用,并且在处理 XML 时往往比使用 XPath 更容易。我最喜欢参考的网站是http://www.hookdonlinq.com/

于 2010-08-18T19:59:51.513 回答