أفضل الممارسات باستخدام خدمات واجهة برمجة التطبيقات للترميز الجغرافي

خدمات الويب في "منصّة خرائط Google" هي مجموعة من واجهات HTTP لخدمات Google التي تقدّم بيانات جغرافية لتطبيقات الخرائط.

يصف هذا الدليل بعض الممارسات الشائعة المفيدة لإعداد طلبات خدمة الويب ومعالجة ردود الخدمة. يُرجى الرجوع إلى دليل المطوّر للاطّلاع على المستندات الكاملة لواجهة برمجة التطبيقات Geocoding API.

ما هي خدمة الويب؟

خدمات الويب في "منصّة خرائط Google" هي واجهة لطلب بيانات Maps API من خدمات خارجية واستخدام البيانات ضمن تطبيقات "خرائط Google". تم تصميم هذه الخدمات لاستخدامها مع خريطة، وفقًا لمواد قيود الترخيص في بنود خدمة "منصة خرائط Google".

تستخدم خدمات الويب في Maps APIs طلبات HTTP(S) لعناوين URL محدّدة، مع تمرير مَعلمات عناوين URL و/أو data POST بتنسيق JSON كوسيطات إلى الخدمات. بشكل عام، تعرض هذه الخدمات البيانات في ملف الاستجابة بتنسيق JSON أو XML لتحليلها و/أو معالجتها من خلال تطبيقك.

يكون طلب Geocoding API النموذجي على النحو التالي:

https://maps.googleapis.com/maps/api/geocode/output?parameters

حيث يشير output إلى تنسيق الردّ (عادةً json أو xml).

ملاحظة: تتطلّب جميع تطبيقات Geocoding API المصادقة. اطّلِع على مزيد من المعلومات عن بيانات اعتماد المصادقة.

الوصول إلى طبقة المقابس الآمنة/بروتوكول أمان طبقة النقل الآمنة

يجب استخدام بروتوكول HTTPS مع جميع طلبات "منصّة خرائط Google" التي تستخدِم مفاتيح واجهة برمجة التطبيقات أو تحتوي على data المستخدم. قد يتم رفض الطلبات التي يتم إجراؤها عبر HTTP والتي تحتوي على بيانات حسّاسة.

إنشاء عنوان URL صالح

قد تعتقد أنّ عنوان URL "صالح" هو أمر واضح، ولكن هذا ليس صحيحًا تمامًا. على سبيل المثال، قد يحتوي عنوان URL الذي يتم إدخاله في شريط العناوين في browser على رموز خاصة (مثل "上海+中國")، ويجب أن يترجم المتصفّح تلك الأحرف داخليًا إلى ترميز مختلف قبل الإرسال. وبالمثل، أي رمز ينشئ إدخال UTF-8 أو يقبله قد يتعامل مع عناوين URL التي تحتوي على أحرف UTF-8 على أنّها "صالحة"، ولكنّه سيحتاج أيضًا إلى ترجمة هذه الأحرف قبل إرسالها إلى خادم ويب. تُعرف هذه العملية باسم ترميز عنوان URL أو ترميز النسبة المئوية.

الرموز الخاصة

نحتاج إلى ترجمة الأحرف الخاصة لأنّه يجب أن تمتثل جميع عناوين URL للبنية المحدّدة في مواصفات معرِّف الموارد المنتظم (URI). وهذا يعني أنّ عناوين URL повинна تتضمّن فقط مجموعة فرعية خاصة من أحرف ASCII: الرموز الأبجدية الرقمية المألوفة، وبعض الأحرف المحجوزة لاستخدامها كرموز التحكّم في عناوين URL. ويلخّص هذا الجدول هذه الأحرف:

ملخّص أحرف عناوين URL الصالحة
تأهّبالأحرفاستخدام عنوان URL
أحرف أبجدية رقمية a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 سلاسل النصوص واستخدام المخطط (http) والمنفذ (8080) وما إلى ذلك
غير محجوز - _ . ~ سلاسل النصوص
تم الحجز ! * ' ( ) ; : @ & = + $ , / ? % # [ ] أحرف التحكّم و/أو السلاسل النصية

عند إنشاء عنوان URL صالح، يجب التأكّد من أنّه يحتوي على الأحرف المعروضة في جدول فقط. يؤدي جعل عنوان URL متوافقًا مع هذه المجموعة من الأحرف بشكل عام إلى حدوث مشكلتَين، إحداهما متعلقة بالإغفال والأخرى متعلقة بالاستبدال:

  • الأحرف التي تريد استخدامها غير متوفّرة في المجموعة أعلاه. على سبيل المثال، يجب ترميز الأحرف المكتوبة بلغات أجنبية مثل 上海+中國 باستخدام الأحرف أعلاه. وفقًا للعرف الشائع، غالبًا ما يتم تمثيل المسافات (التي لا يُسمح بها ضمن عناوين URL) باستخدام الحرف '+' بدلاً من الرمز (+).
  • تظهر الأحرف ضمن المجموعة أعلاه كأحرف محجوزة، ولكن يجب استخدامها حرفيًا. على سبيل المثال، يتم استخدام الرمز ? ضمن عناوين URL للإشارة إلى بداية سلسلة طلب البحث. إذا كنت تريد استخدام السلسلة "? and the Mysterions"، عليك ترميز الحرف '?'.

يتم ترميز جميع الأحرف التي سيتم ترميزها باستخدام عنوان URL باستخدام الحرف '%' وقيمة سداسية تشكل حرفين تتوافق مع حرف UTF-8. على سبيل المثال، سيتم ترميز ‎ 上海+中國 بترميز UTF-8 في عنوان URL على النحو التالي: ‎ %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. سيتم ترميز السلسلة ? and the Mysterians بترميز عنوان URL على النحو التالي: %3F+and+the+Mysterians أو %3F%20and%20the%20Mysterians.

الأحرف الشائعة التي تحتاج إلى ترميز

في ما يلي بعض الأحرف الشائعة التي يجب ترميزها:

حرف غير آمن القيمة المشفّرة
مسافة %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

قد يكون أحيانًا تحويل عنوان URL الذي تتلقّاه من إدخال المستخدمين معقدًا. على سبيل المثال، قد يُدخل المستخدم عنوانًا على النحو التالي: "شارع 5&Main". بشكل عام، يجب إنشاء عنوان URL من أجزائه، مع التعامل مع أي إدخال من المستخدِم كأحرف حرفية.

بالإضافة إلى ذلك، تقتصر عناوين URL على 16384 حرفًا لجميع خدمات ويب Google Maps Platform وواجهات برمجة تطبيقات الويب الثابتة. في معظم الخدمات، نادرًا ما يتم الاقتراب من هذا الحدّ الأقصى لعدد الأحرف. ومع ذلك، يُرجى العِلم أنّ بعض الخدمات تتضمّن عدة مَعلمات قد تؤدي إلى ظهور عناوين URL طويلة.

الاستخدام اللائق لواجهات برمجة تطبيقات Google

يمكن أن تؤدي برامج عملاء واجهات برمجة التطبيقات المصمّمة بشكلٍ سيئ إلى زيادة الحمل على الإنترنت و خوادم Google أكثر من اللازم. يحتوي هذا القسم على بعض أفضل الممارسات لعملاء واجهات برمجة التطبيقات. يمكن أن يساعدك اتّباع هذه أفضل الممارسات في تجنُّب حظر تطبيقك بسبب إساءة استخدام واجهات برمجة التطبيقات بدون قصد.

إدارة الأخطاء وعمليات إعادة المحاولة

للحصول على معلومات عن رموز الاستجابة UNKNOWN_ERROR أو OVER_QUERY_LIMIT من Geocoding API، اطّلِع على مقالة إدارة الأخطاء وعمليات إعادة المحاولة.

الرقود الأسي

في حالات نادرة، قد يحدث خطأ أثناء عرض طلبك، وقد تتلقّى رمز استجابة HTTP‏ 4XX أو 5XX، أو قد يتعذّر الاتصال عبر بروتوكول TCP في مكان ما بين العميل وخادم Google. غالبًا ما يكون من المفيد إعادة محاولة إرسال الطلب، لأنّه قد ينجح طلب المتابعة عندما يتعذّر إرسال الطلب الأصلي. ومع ذلك، من المهم عدم إجراء طلبات متكرّرة إلى خوادم Google. يمكن أن يؤدي هذا السلوك المتكرّر إلى زيادة الحمل على الشبكة بين برنامجك وGoogle، ما يتسبب في مشاكل للعديد من الجهات.

من الطرق الأفضل إعادة المحاولة مع زيادة فترات الانتظار بين المحاولات. عادةً ما يتم زيادة التأخير بمقدار مضاعف مع كل محاولة، وهو نهج يُعرف باسم الرقود الأسي الثنائي.

على سبيل المثال، لنفترض أنّ هناك تطبيقًا يريد إرسال هذا الطلب إلى Time Zone API:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

يوضّح مثال Python التالي كيفية تقديم الطلب باستخدام ميزة "التوقّف المؤقت للانتظار العشوائي":

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

يجب أيضًا الحرص على عدم توفّر رمز إعادة محاولة أعلى في سلسلة طلبات التطبيق مما يؤدي إلى تكرار الطلبات بشكل سريع.

الطلبات المتزامنة

يمكن أن تبدو أعداد كبيرة من الطلبات المتزامنة إلى واجهات برمجة تطبيقات Google كهجوم هجمات ازدحام هجمات ازدحام مُوزَّعة (DDoS) على البنية الأساسية لشركة Google، ويتم التعامل معها وفقًا لذلك. لتجنُّب ذلك، يجب التأكّد من عدم مزامنة طلبات البيانات من واجهة برمجة التطبيقات بين العملاء.

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

إنّ إجراء طلبات البيانات من واجهة برمجة التطبيقات استجابةً لتنبيه ثابت هو أمر غير جيد، لأنّه يؤدي إلى تتميم مزامنة طلبات البيانات من واجهة برمجة التطبيقات مع بداية الدقيقة، حتى بين الأجهزة المختلفة، بدلاً من توزيعها بالتساوي بمرور الوقت. سيؤدي تطبيق مصمّم بشكلٍ سيئ إلى حدوث قفزة في عدد الزيارات بمقدار ستين مرة عن المستويات العادية في بداية كل دقيقة.

بدلاً من ذلك، يمكن ضبط منبّه ثانٍ على وقت يتم اختياره عشوائيًا. عند تشغيل هذا التنبيه الثاني، يستدعي التطبيق أي واجهات برمجة تطبيقات يحتاج إليها ويخزّن النتائج. عندما يريد التطبيق تعديل الشاشة في بداية الدقيقة، يستخدم النتائج المخزّنة سابقًا بدلاً من طلب البيانات من واجهة برمجة التطبيقات مرة أخرى. وباستخدام هذا النهج، يتم توزيع طلبات بيانات واجهة برمجة التطبيقات بالتساوي على مدار الوقت. بالإضافة إلى ذلك، لا تؤخّر طلبات البيانات من واجهة برمجة التطبيقات عرض المحتوى عند تعديل الشاشة.

بالإضافة إلى بداية الدقيقة، هناك أوقات مزامنة شائعة أخرى يجب الحذرمن استهدافها، وهي بداية الساعة وبداية كل يوم عند منتصف الليل.

معالجة الردود

يتناول هذا القسم كيفية استخراج هذه القيم ديناميكيًا من ردود خدمات الويب.

توفّر خدمات الويب في "خرائط Google" ردودًا سهلة فهمها، ولكنها ليست سهلة الاستخدام. عند إجراء استعلام، بدلاً من عرض مجموعة من البيانات، قد تحتاج إلى استخراج بعض القيم المحدّدة. بشكل عام، ستحتاج إلى تحليل الردود الواردة من خدمة الويب واستخراج القيم التي تهمّك فقط.

يعتمد مخطّط التحليل الذي تستخدمه على ما إذا كنت تعرض النتيجة بتنسيق XML أو JSON. يمكن معالجة استجابات JSON، التي تكون في شكل كائنات JavaScript، ضمن JavaScript نفسه على العميل. يجب معالجة الردود بتنسيق XML باستخدام معالج XML ولغة استعلامات XML لمعالجة العناصر ضمن تنسيق XML. نستخدم XPath في المثالين التاليين، لأنّه متاح بشكل شائع في مكتبات معالجة XML.

معالجة ملف XML باستخدام XPath

XML هو تنسيق معلومات منظَّمة قديم نسبيًا يُستخدَم لتبادل البيانات. على الرغم من أنّه ليس خفيفًا مثل JSON، يقدّم XML أدوات أكثر فعالية ودعمًا لعدد أكبر من اللغات. على سبيل المثال، تم تضمين رمز برمجي لمعالجة XML في Java في حزم javax.xml.

عند معالجة الردود بتنسيق XML، يجب استخدام لغة طلب بحث مناسبة لاختيار العقد داخل ملف XML، بدلاً من افتراض أنّ العناصر تقع في مواضع مطلقة ضمن علامة XML. XPath هو بنية لغوية لوصف العقد والعناصر بشكل فريد في ملف XML. تسمح لك تعبيرات XPath بتحديد محتوى معيّن ضمن مستند الاستجابة بتنسيق XML.

تعبيرات XPath

إنّ بعض المعرفة بـ XPath تساهم بشكل كبير في تطوير مخطّط تحليل قوي. سيتمحور هذا القسم حول كيفية توجيه العناصر داخل مستند XML باستخدام XPath، ما يتيح لك توجيه عناصر متعدّدة وإنشاء طلبات بحث معقّدة.

يستخدم XPath تعبيرات لاختيار العناصر ضمن مستند XML، باستخدام بنية مشابهة لتلك المستخدَمة لمسارات الدلائل. وتحدِّد هذه التعبيرات العناصر ضمن شجرة مستند XML، وهي شجرة هرمية مشابهة لشجرة نموذج DOM. بشكل عام، تكون تعبيرات XPath جشعة، ما يعني أنّها ستتطابق مع جميع العقد التي تتطابق مع المعايير المقدَّمة.

سنستخدم ملف XML التالي لتوضيح أمثلةنا:

<WebServiceResponse>
 <status>OK</status>
 <result>
  <type>sample</type>
  <name>Sample XML</name>
  <location>
   <lat>37.4217550</lat>
   <lng>-122.0846330</lng>
  </location>
 </result>
 <result>
  <message>The secret message</message>
 </result>
</WebServiceResponse>

اختيار العقد في التعبيرات

تختار اختيارات XPath العناصر. تشمل العقدة الجذر المستند بأكمله. يمكنك اختيار هذه العقدة باستخدام التعبير الخاص "/". يُرجى العِلم أنّ node الجذر ليس العقدة ذات المستوى الأعلى من مستند XML، بل تقع في مستوى أعلى من هذا العنصر ذي المستوى الأعلى وتتضمّنه.

تمثّل عقد العناصر العناصر المختلفة ضمن شجرة مستند XML. على سبيل المثال، يمثّل عنصر <WebServiceResponse> العنصر ذي المستوى الأعلى الذي تم إرجاعه في <WebServiceResponse>نموذج الخدمة أعلاه. يمكنك اختيار عقد فردية إما من خلال مسارات مطلقة أو نسبية، يُشار إليها من خلال وجود أو عدم وجود الحرف "/" في بداية المسار.

  • المسار المطلق: يختار التعبير "/WebServiceResponse/result" جميع عقد <result> التي تكون فرعية لعقدة <WebServiceResponse>. (يُرجى العِلم أنّ كلا العنصرَين ينحدران من العقدة "/" الجذر ).
  • مسار نسبي من السياق الحالي: سيطابق التعبير "result" أي عناصر <result> ضمن السياق الحالي. بشكل عام، لا ينبغي أن تقلق بشأن السياق، لأنّك عادةً ما تعالج نتائج خدمة الويب من خلال تعبير واحد.

يمكن تحسين أيّ من هذين التعبيرَين من خلال إضافة مسار حرف بدل، يُشار إليه بشريطَين متقاطعَين ("//"). يشير حرف البدل هذا إلى أنّه قد تتطابق صفر عناصر أو أكثر في المسار الفاصل. على سبيل المثال، سيتطابق تعبير XPath "//formatted_address" مع جميع العقد التي تحمل هذا الاسم في المستند الحالي. سيتطابق التعبير //viewport//lat مع جميع عناصر <lat> التي يمكنها تتبُّع<viewport> كعنصر رئيسي.

تطابق عبارات XPath تلقائيًا جميع العناصر. يمكنك تقييد التعبير لمطابقة عنصر معيّن من خلال تقديم عبارة تحديد، التي تكون محاطة بقوسين مربّعين ([]). على سبيل المثال، يعرض تعبير XPath "/GeocodeResponse/result[2]" دائمًا النتيجة الثانية.

نوع التعبير
Root node
تعبير XPath:  "/"
الاختيار:
    <WebServiceResponse>
     <status>OK</status>
     <result>
      <type>sample</type>
      <name>Sample XML</name>
      <location>
       <lat>37.4217550</lat>
       <lng>-122.0846330</lng>
      </location>
     </result>
     <result>
      <message>The secret message</message>
     </result>
    </WebServiceResponse>
    
المسار المطلق
تعبير XPath:  "/WebServiceResponse/result"
الاختيار:
    <result>
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    </result>
    <result>
     <message>The secret message</message>
    </result>
    
مسار يتضمّن حرف بدل
تعبير XPath:  "/WebServiceResponse//location"
الاختيار:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
مسار مع تعبير وصفي
تعبير XPath:  "/WebServiceResponse/result[2]/message"
الاختيار:
    <message>The secret message</message>
    
جميع العناصر الثانوية المباشرة للعنصر result الأول
تعبير XPath:  "/WebServiceResponse/result[1]/*"
الاختيار:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
name من result الذي يكون نص type فيه "sample"
تعبير XPath:  "/WebServiceResponse/result[type/text()='sample']/name"
الاختيار:
    Sample XML
    

من المهم ملاحظة أنّه عند اختيار العناصر، يتم اختيار العقد، وليس النص فقط ضمن هذه الكائنات. بشكل عام، ستحتاج إلى تكرار جميع العقد المطابقة واستخراج النص. يمكنك أيضًا مطابقة عقد النصوص مباشرةً، راجِع عقد النصوص أدناه.

يُرجى العِلم أنّ XPath يتيح استخدام عقد السمات أيضًا، ومع ذلك، فإنّ جميع خدمات ويب "خرائط Google" تعرِض عناصر بدون سمات، لذلك لا يلزم مطابقة السمات.

اختيار النص في التعبيرات

يتم تحديد النص ضمن مستند XML في تعبيرات XPath من خلال عامل تشغيل عقدة نص. يشير عامل التشغيل "text()" إلى استخراج نص من العقدة المُشار إليها. على سبيل المثال، سيعرض تعبير XPath "//formatted_address/text()" كل النص ضمن عناصر<formatted_address>.

نوع التعبير
جميع عقد النصوص (بما في ذلك المسافات البيضاء)
تعبير XPath:  "//text()"
الاختيار:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
اختيار النص
تعبير XPath:  "/WebServiceRequest/result[2]/message/text()"
الاختيار:
    The secret message
    
الاختيار حسب السياق
تعبير XPath:  "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
الاختيار:
    Sample XML
    

بدلاً من ذلك، يمكنك تقييم تعبير وعرض مجموعة من العقد ثم تكرار "مجموعة العقد" هذه، واستخراج النص من كل عقدة. نستخدم هذا النهج في المثال أدناه.

لمزيد من المعلومات حول XPath، يُرجى الرجوع إلى مواصفات XPath من W3C.

تقييم XPath في Java

تتيح Java إمكانية واسعة لتحليل ملفات XML واستخدام تعبيرات XPath ضمن حزمة javax.xml.xpath.*. لهذا السبب، يستخدم نموذج الرمز البرمجي في هذا القسم لغة Java لشرح كيفية التعامل مع تنسيق XML وتحليل البيانات من ردود خدمة XML.

لاستخدام XPath في رمز Java، عليك أولاً إنشاء مثيل لعنصر XPathFactory ثم استدعاء newXPath() في هذا المصنع لإنشاء عنصر XPath . يمكن لهذا الكائن بعد ذلك معالجة تعبيرات XML وXPath التي تم تمريرها باستخدام الطريقة evaluate().

عند تقييم تعبيرات XPath، تأكَّد من تكرار أيّ "مجموعات عقد" محتملة قد يتم عرضها. وبما أنّه يتم عرض هذه النتائج كنوادي DOM في رمز Java، عليك تسجيل هذه القيم المتعدّدة ضمن عنصر NodeList و تكرار هذا العنصر لاستخراج أي نص أو قيم من تلك العناصر.

توضِّح التعليمة البرمجية التالية كيفية إنشاء عنصر XPath وتخصيص تعبير XML وتعبير XPath له، وتقييم تعبير لطباعة المحتوى ذي الصلة.

import org.xml.sax.InputSource;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import java.io.*;

public class SimpleParser {

  public static void main(String[] args) throws IOException {

	XPathFactory factory = XPathFactory.newInstance();

    XPath xpath = factory.newXPath();

    try {
      System.out.print("Web Service Parser 1.0\n");

      // In practice, you'd retrieve your XML via an HTTP request.
      // Here we simply access an existing file.
      File xmlFile = new File("XML_FILE");

      // The xpath evaluator requires the XML be in the format of an InputSource
	  InputSource inputXml = new InputSource(new FileInputStream(xmlFile));

      // Because the evaluator may return multiple entries, we specify that the expression
      // return a NODESET and place the result in a NodeList.
      NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET);

      // We can then iterate over the NodeList and extract the content via getTextContent().
      // NOTE: this will only return text for element nodes at the returned context.
      for (int i = 0, n = nodes.getLength(); i < n; i++) {
        String nodeString = nodes.item(i).getTextContent();
        System.out.print(nodeString);
        System.out.print("\n");
      }
    } catch (XPathExpressionException ex) {
	  System.out.print("XPath Error");
    } catch (FileNotFoundException ex) {
      System.out.print("File Error");
    }
  }
}