سؤال

أحاول تحليل بعض المعلومات من واجهة برمجة تطبيقات الجغرافية من Google ، لكنني أواجه مشكلة صغيرة في إخراج البيانات من XML بكفاءة. انظر الرابط على سبيل المثال

كل ما أهتم به حقًا هو الحصول على short_name من address_component حيث النوع administrative_area_level_1 و ال long_name من administrative_area_level_2ولكن مع برنامج الاختبار الخاص بي ، لا يعيد استعلام XPath أي نتائج لكلا الاستعلامات.

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

        }
    }
}

هل يمكن لأي شخص أن يساعدني في العثور على ما أفعله خطأ ، أو يوصي طريقة أفضل؟

هل كانت مفيدة؟

المحلول

تحتاج إلى وضع قيمة العقدة التي تبحث عنها في اقتباسات:

".../address_component[type='administrative_area_level_1']/short_name"
                            ↑                           ↑

نصائح أخرى

بالتأكيد أوصي باستخدام LINQ إلى XML بدلاً من XPathNavigator. يجعل XML يستعلم نسيم ، في تجربتي. في هذه الحالة ، لست متأكدًا تمامًا من الخطأ ... لكنني سأتوصل إلى مقتطف LINQ إلى 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 ، على الرغم من أنه من الواضح أنه أطول.

عندما تستخدم XPath داخل برنامج C# ، لديك لغتين مختلفتين - ولكن واحدة فقط هي في التحكم (C#). يتم نقل XPath إلى عالم الأوتار: Visual Studio لا يعطي تعبير XPath أي معالجة خاصة ؛ لا تفهم من المفترض أن يكون تعبير XPath ، لذلك لا يمكن أن يساعدك. ليس الأمر أن Visual Studio لا يعرف عن XPath ؛ كما يشير Dimitre ، فإنه قادر تمامًا على اكتشاف الأخطاء إذا كنت تقوم بتحرير ملف XSLT ، وليس فقط ملف C#.

هذا هو الحال كلما كان لديك لغة مضمنة داخل آخر والأداة غير مدركة لها. الأمثلة الشائعة هي:

  • SQL
  • تعبيرات منتظمة
  • لغة البرمجة
  • xpath

عندما يتم تقديم الكود كبيانات داخل لغة أخرى ، تفقد اللغة الثانوية الكثير من مزايا الأدوات الخاصة بها.

بينما أنت يستطيع تبديل السياق في كل مكان ، أو سحب XPath (أو SQL ، أو التعبيرات العادية وما إلى ذلك) في أدواتهم الخاصة (ربما داخل نفس البرنامج الفعلي ، ولكن في ملف أو نافذة منفصلة) أجد أن هذا يجعله أكثر صعوبة في القراءة رمز على المدى الطويل. إذا تم كتابة الرمز على الإطلاق ولم يقرأ بعد ذلك ، فقد يكون ذلك على ما يرام - لكنك فعل يجب أن تكون قادرًا على قراءة التعليمات البرمجية بعد ذلك ، وأعتقد شخصياً أن قابلية القراءة تعاني عندما يحدث هذا.

يستخدم إصدار LINQ إلى XML أعلاه فقط سلاسل للبيانات الخالصة - أسماء العناصر وما إلى ذلك - ويستخدم الكود (مكالمات الطريقة) لتمثيل إجراءات مثل "العثور على عناصر تحمل اسمًا معينًا" أو "تطبيق هذا المرشح". هذا أكثر احصائية C# رمز ، في رأيي.

من الواضح أن الآخرين لا يشاركون وجهة النظر هذه ، لكنني اعتقدت أن الأمر يستحق التوسع لإظهار من أين أتيت.

لاحظ أن هذا ليس صعبًا وسريعًا قاعدة بالطبع ... في بعض الحالات ، تعتبر التعبيرات العادية وما إلى ذلك هي الحل الأفضل. في هذه الحالة ، أفضل LINQ إلى XML ، هذا كل شيء.


1 بالطبع أنا استطاع احتفظت بكل Console.WriteLine اتصل بسطر واحد ، لكنني لا أحب نشر الرمز باستخدام أشرطة التمرير الأفقية على ذلك. لاحظ أن كتابة إصدار XPath الصحيح مع نفس المسافة البادئة مثل ما سبق وتجنب التمرير لا يزال سيئًا للغاية:

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

بشكل عام ، تعمل الخطوط الطويلة بشكل أفضل في Visual Studio أكثر مما تعمل في سعة مكدس ...

أوصي فقط بكتابة تعبير XPath كجزء من ملف XSLT في Visual Studio. ستحصل على رسائل خطأ "أثناء الكتابة" - هذا محرر ممتاز 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# الخاص بي.

هذا يضمن عدم وجود أخطاء Xpath - بناء الجملة والدليلية - عندما أقوم بتشغيل برنامج C#.

استجابة DTB دقيقة. أردت أن أضيف أنه يمكنك استخدام أدوات اختبار XPath مثل الرابط أدناه للمساعدة في العثور على XPath الصحيح:

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

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 بشكل عام لأنه مفيد جدًا لمعالجة الكائنات في الذاكرة والاستعلام عنها ، والاستعلام عن قواعد البيانات ويميل إلى أن تكون أسهل من استخدام XPath عند العمل مع XML. موقعي المفضل للرجوع إليه http://www.hookedonlinq.com/

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top