מבוא ל- JsonPath

1. סקירה כללית

אחד היתרונות של XML הוא זמינות העיבוד - כולל XPath - שמוגדרת כתקן W3C. עבור JSON, הופיע כלי דומה בשם JSONPath.

מאמר זה ייתן היכרות עם Jayway JsonPath, יישום Java של מפרט JSONPath. הוא מתאר התקנה, תחביר, ממשקי API נפוצים והדגמה של מקרי שימוש.

2. התקנה

כדי להשתמש ב- JsonPath, עלינו פשוט לכלול תלות ב- Maven pom:

 com.jayway.jsonpath json-path 2.4.0 

3. תחביר

מבנה JSON הבא ישמש בחלק זה להדגמת התחביר וה- API של JsonPath:

{"tool": {"jsonpath": {"creator": {"name": "Jayway Inc.", "location": ["Malmo", "San Francisco", "Helsingborg"]}}, "book ": [{" title ":" JSON מתחיל "," price ": 49.99}, {" title ":" JSON at Work "," price ": 29.99}]}

3.1. סִמוּן

JsonPath משתמש בסימון מיוחד כדי לייצג צמתים ואת קשריהם לצמתים סמוכים בנתיב JsonPath. ישנם שני סגנונות של סימון, כלומר נקודה וסוגר.

שני הנתיבים הבאים מתייחסים לאותו צומת ממסמך JSON לעיל, שהוא האלמנט השלישי בתוך ה- מקום שדה של בורא הצומת, שהוא ילד של jsonpath אובייקט השייך כְּלִי מתחת לצומת השורש.

עם סימון נקודה:

$ .tool.jsonpath.creator.location [2]

עם סימון סוגריים:

$ ['tool'] ['jsonpath'] ['יוצר'] ['מיקום'] [2]

סימן הדולר ($) מייצג אובייקט חבר שורש.

3.2. מפעילים

יש לנו מספר מפעילים מועילים ב- JsonPath:

צומת שורש ($): סמל זה מציין את איבר השורש של מבנה JSON, לא משנה שזה אובייקט או מערך. דוגמאות השימוש בו נכללו בסעיף המשנה הקודם.

צומת נוכחי (@): מייצג את הצומת המעובד, משמש בעיקר כחלק מביטויי קלט לפרדיקטים. נניח שאנחנו מתמודדים עם סֵפֶר מערך במסמך JSON לעיל, הביטוי ספר [? (@. מחיר == 49.99)] מתייחס לראשון סֵפֶר במערך ההוא.

תו כללי (*): מבטא את כל האלמנטים בהיקף שצוין. לדוגמה, סֵפֶר[*] מציין את כל הצמתים בתוך a סֵפֶר מַעֲרָך.

3.3. פונקציות ומסננים

ל- JsonPath יש פונקציות שניתן להשתמש בהן עד סוף הנתיב לסינתזת ביטויי הפלט של הנתיב: דקה (), מקסימום (), ממוצע (), stddev (), אורך().

לבסוף - יש לנו פילטרים; אלה ביטויים בוליאניים להגבלת רשימות הצמתים שהוחזרו רק לאלה הדרושות לשיטות קריאה.

כמה דוגמאות הן שוויון (==), התאמת ביטוי רגיל (=~), הכללה (ב), בדוק אם יש ריקנות (ריק). מסננים משמשים בעיקר לפרדיקטים.

לקבלת רשימה מלאה והסברים מפורטים על אופרטורים, פונקציות ומסננים שונים, עיין בפרויקט JsonPath GitHub.

4. פעולות

לפני שנכנס לפעולות, הערת צד מהירה - סעיף זה עושה שימוש במבנה הדוגמה של JSON שהגדרנו קודם.

4.1. גישה למסמכים

ל- JsonPath יש דרך נוחה לגשת למסמכי JSON, דרך סטטי לקרוא ממשקי API:

 T JsonPath.read (String jsonString, String jsonPath, Predicate ... filters);

ה לקרוא ממשקי API יכולים לעבוד עם ממשקי API רהוטים סטטיים כדי לספק גמישות רבה יותר:

 T JsonPath.parse (String jsonString) .read (String jsonPath, Predicate ... filters);

גרסאות עמוסות אחרות של לקרוא יכול לשמש לסוגים שונים של מקורות JSON, כולל לְהִתְנַגֵד, InputStream, כתובת אתר, ו קוֹבֶץ.

כדי להפוך את הדברים לפשוטים, המבחן לחלק זה אינו כולל פרדיקטים ברשימת הפרמטרים (ריק varargs); predicates יידונו בחלקי המשנה המאוחרים יותר.

נתחיל בהגדרת שני נתיבי דוגמה לעבודה:

מחרוזת jsonpathCreatorNamePath = "$ ['tool'] ['jsonpath'] ['creator'] ['name']"; מחרוזת jsonpathCreatorLocationPath = "$ ['tool'] ['jsonpath'] ['creator'] ['location'] [*]";

לאחר מכן, ניצור DocumentContext אובייקט על ידי ניתוח מקור ה- JSON הנתון jsonDataSourceString. האובייקט החדש שנוצר ישמש לקריאת תוכן באמצעות הנתיבים שהוגדרו לעיל:

DocumentContext jsonContext = JsonPath.parse (jsonDataSourceString); מחרוזת jsonpathCreatorName = jsonContext.read (jsonpathCreatorNamePath); רשימה jsonpathCreatorLocation = jsonContext.read (jsonpathCreatorLocationPath);

הראשון לקרוא API מחזיר a חוּט המכיל את שמו של יוצר JsonPath, ואילו השני מחזיר רשימה של כתובותיו. ונשתמש ב- JUnit לִטעוֹן API לאישור השיטות פועלות כצפוי:

assertEquals ("Jayway Inc.", jsonpathCreatorName); assertThat (jsonpathCreatorLocation.toString (), containString ("Malmo")); assertThat (jsonpathCreatorLocation.toString (), containString ("סן פרנסיסקו")); assertThat (jsonpathCreatorLocation.toString (), containString ("הלסינגבורג"));

4.2. מנבא

עכשיו, לאחר שסיימנו את היסודות, בואו נגדיר דוגמה חדשה של JSON לעבודה ולהמחשה על יצירה ושימוש של פרדיקטים:

{"book": [{"title": "JSON מתחיל", "author": "Ben Smith", "price": 49.99}, {"title": "JSON at Work", "author": "Tom Marrs "," price ": 29.99}, {" title ":" למד את JSON ביום "," author ":" Acodemy "," price ": 8.99}, {" title ":" JSON: שאלות ותשובות ", "author": "George Duckett", "price": 6.00}], "טווח מחירים": {"זול": 10.00, "medium": 20.00}}

פרדיקטים קובעים ערכי קלט אמיתיים או שקריים עבור מסננים כדי לצמצם רשימות שהוחזרו לאובייקטים או מערכים תואמים בלבד. א לְבַסֵס עשוי להשתלב בקלות ב- לְסַנֵן על ידי שימוש כטיעון לשיטת המפעל הסטטית שלה. לאחר מכן ניתן לקרוא את התוכן המבוקש מתוך מחרוזת JSON באמצעות זה לְסַנֵן:

סינון יקר Filter = Filter.filter (Criteria.where ("מחיר"). Gt (20.00)); רשימה יקר = JsonPath.parse (jsonDataSourceString) .read ("$ ['ספר'] [?]", יקר פילטר); predicateUsageAssertionHelper (יקר);

אנו עשויים גם להגדיר את ההתאמה האישית שלנו לְבַסֵס ולהשתמש בו כטיעון עבור לקרוא ממשק API:

Predicate expensivePredicate = Predicate new () {בוליאני ציבורי חל (הקשר PredicateContext) {ערך מחרוזת = context.item (Map.class) .get ("מחיר"). ToString (); להחזיר Float.valueOf (value)> 20.00; }}; רשימה יקר = JsonPath.parse (jsonDataSourceString) .read ("$ ['ספר'] [?]", יקר פרדיקט); predicateUsageAssertionHelper (יקר);

לבסוף, ניתן להחיל ישירות על פרדיקט לקרוא API ללא יצירת אובייקטים כלשהם, הנקראים פרדיקט מוטבע:

רשימה יקר = JsonPath.parse (jsonDataSourceString). קרא ("$ ['ספר'] [? (@ ['מחיר']> $ ['טווח מחירים'] ['בינוני']]]"); predicateUsageAssertionHelper (יקר);

כל שלושת ה לְבַסֵס הדוגמאות לעיל מאומתות בעזרת שיטת העזר הקביעה הבאה:

predicateUidageUsageAssertionHelper (predicate List) {assertThat (predicate.toString (), containString ("JSON מתחיל")); assertThat (predicate.toString (), containString ("JSON בעבודה")); assertThat (predicate.toString (), לא (containString ("למד JSON ביום"))); assertThat (predicate.toString (), לא (containString ("JSON: שאלות ותשובות"))); }

5. תצורה

5.1. אפשרויות

Jayway JsonPath מספק מספר אפשרויות לשינוי תצורת ברירת המחדל:

  • אפשרות. AS_PATH_LIST: מחזירה נתיבים של התאמות ההערכה במקום הערכים שלהם.
  • אפשרות.DEFAULT_PATH_LEAF_TO_NULL: מחזיר אפס עבור עלים חסרים.
  • אפשרות. ALWAYS_RETURN_LIST: מחזירה רשימה גם כאשר הנתיב מוגדר.
  • אפשרות.SUPPRESS_EXCEPTIONS: מוודא שלא מפיצים חריגים מהערכת מסלול.
  • Option.REQUIRE_PROPERTIES: דורש מאפיינים המוגדרים בנתיב כאשר מעריכים נתיב בלתי מוגדר.

הנה איך אוֹפְּצִיָה מוחל מאפס:

תצורת תצורה = Configuration.builder (). אפשרויות (אפשרות.). Build ();

וכיצד להוסיף אותו לתצורה קיימת:

תצורה newConfiguration = configuration.addOptions (אפשרות.);

5.2. SPIs

תצורת ברירת המחדל של JsonPath בעזרת אוֹפְּצִיָה אמור להספיק לרוב המשימות. עם זאת, משתמשים עם מקרי שימוש מורכבים יותר יכולים לשנות את ההתנהגות של JsonPath בהתאם לדרישות הספציפיות שלהם - באמצעות שלושה SPIs שונים:

  • JsonProvider SPI: מאפשר לנו לשנות את האופן שבו JsonPath מנתח ומטפל במסמכי JSON
  • MappingProvider SPI: מאפשר התאמה אישית של כריכות בין ערכי צומת לסוגי אובייקטים שהוחזרו
  • ספק מטמון SPI: מכוון את הנימוסים שמורים במטמון, אשר יכולים לעזור להגדיל את הביצועים

6. דוגמאות למקרי שימוש

עכשיו שיש לנו הבנה טובה של הפונקציונליות שאליה ניתן להשתמש ב- JsonPath - בואו נסתכל על דוגמא.

סעיף זה ממחיש את הטיפול בנתוני JSON שהוחזרו משירות אינטרנט - נניח שיש לנו שירות מידע על סרטים, המחזיר את המבנה הבא:

[{"id": 1, "title": "קזינו רויאל", "במאי": "מרטין קמפבל", "בכיכובו:" ["דניאל קרייג", "אווה גרין"], "desc": "עשרים ואחת סרט ג'יימס בונד "," תאריך יציאה ": 1163466000000," קופות ": 594275385}, {" id ": 2," title ":" Quantum of Solace "," במאי ":" מארק פורסטר "," בכיכובו: " ["דניאל קרייג", "אולגה קורילנקו"], "desc": "עשרים ושניים סרט ג'יימס בונד", "תאריך יציאה": 1225242000000, "קופות": 591692078}, {"id": 3, "title" : "Skyfall", "במאי": "סם מנדס", "בכיכובו": ["דניאל קרייג", "נעמי האריס"], "desc": "עשרים ושלושה סרט ג'יימס בונד", "תאריך יציאה": 1350954000000, "קופות": 1110526981}, {"id": 4, "title": "Spectre", "במאי": "סם מנדס", "בכיכובו": ["דניאל קרייג", "לאה סיידוקס"], "desc ":" עשרים וארבע סרט ג'יימס בונד "," תאריך יציאה ": 1445821200000," קופות ": 879376275}]

איפה הערך של תאריך הוצאה השדה הוא משך התקופה באלפיות השנייה קוּפָּה הוא הכנסות של סרט בקולנוע בדולרים אמריקאים.

אנו הולכים לטפל בחמישה תרחישי עבודה שונים הקשורים לבקשות GET, בהנחה שההיררכיה של JSON לעיל חולצה ואוחסנה בתוך חוּט משתנה בשם jsonString.

6.1. קבלת מזהים נתוני אובייקט

במקרה שימוש זה, לקוח מבקש מידע מפורט על סרט ספציפי על ידי מתן המדויק לשרת תְעוּדַת זֶהוּת של אותו אחד. דוגמה זו מדגימה כיצד השרת מחפש נתונים מבוקשים לפני שהוא חוזר ללקוח.

נגיד שאנחנו צריכים למצוא שיא עם תְעוּדַת זֶהוּת השווה ל- 2. להלן כיצד מיושם ונבדק התהליך.

השלב הראשון הוא לאסוף את אובייקט הנתונים הנכון:

אובייקט dataObject = JsonPath.parse (jsonString). קרא ("$ [? (@. Id == 2)]"); מחרוזת dataString = dataObject.toString ();

ה- JUnit לִטעוֹן API מאשר את קיומם של מספר תחומים:

assertThat (dataString, containString ("2")); assertThat (dataString, containString ("קוונטי של נחמה")); assertThat (dataString, containString ("עשרים ושניים סרט ג'יימס בונד"));

6.2. קבלת כותר הסרט בכיכובו

נניח שאנחנו רוצים לחפש סרט בכיכובה של שחקנית בשם אווה גרין. השרת צריך לחזור כותרת של הסרט ש אווה גרין כלול ב מככב מַעֲרָך.

המבחן הבא ימחיש כיצד לעשות זאת ותאמת את התוצאה שהוחזרה:

@Test הציבור בטל givenStarring_whenRequestingMovieTitle_thenSucceed () {רשימה dataList = JsonPath.parse (jsonString) .read ("$ [? ('אווה גרין' ב- @ ['מככב'])"); כותרת מחרוזת = (מחרוזת) dataList.get (0) .get ("כותרת"); assertEquals ("קזינו רויאל", כותרת); }

6.3. חישוב סך ההכנסות

תרחיש זה עושה שימוש בפונקציה JsonPath הנקראת אורך() כדי להבין את מספר רשומות הסרטים, כדי לחשב את ההכנסה הכוללת של כל הסרטים. היישום והבדיקה מודגמים באופן הבא:

@Test הציבור בטל givenCompleteStructure_whenCalculatingTotalRevenue_thenSucceed () {DocumentContext context = JsonPath.parse (jsonString); int אורך = context.read ("$. אורך ()"); הכנסה ארוכה = 0; עבור (int i = 0; i <length; i ++) {הכנסה + = context.read ("$ [" + i + "] ['קופה']", Long.class); } assertEquals (594275385L + 591692078L + 1110526981L + 879376275L, הכנסות); }

6.4. סרט ההכנסות הגבוה ביותר

מקרה שימוש זה מדגים את השימוש באפשרות תצורה שאינה ברירת מחדל של JsonPath, כלומר אפשרות. AS_PATH_LIST, כדי לגלות את הסרט עם ההכנסות הגבוהות ביותר. השלבים המסוימים מתוארים להלן.

בהתחלה, עלינו לחלץ רשימה של כל הכנסות הקופות של הסרטים, ואז להמיר אותה למערך למיון:

DocumentContext context = JsonPath.parse (jsonString); רשימת הכנסות רשימת = context.read ("$ [*] ['קופה']"); מספר שלם [] incomeArray = incomeList.toArray (מספר שלם חדש [0]); Arrays.sort (incomeArray);

ה הכנסה הגבוהה ביותר משתנה עשוי להילקח בקלות מה- הכנסה מערך מערך ממוין, ואז משמש לפיתוח הנתיב לרשומת הסרט עם ההכנסות הגבוהות ביותר:

int highestRevenue = incomeArray [revenArray.length - 1]; Configuration pathConfiguration = Configuration.builder (). אפשרויות (Option.AS_PATH_LIST) .build (); רשימה pathList = JsonPath.using (pathConfiguration) .parse (jsonString) .read ("$ [? (@ ['קופה'] ==" + הכנסה הגבוהה ביותר + ")]");

בהתבסס על הנתיב המחושב הזה, כותרת של הסרט המתאים ניתן לקבוע ולהחזיר:

DataRecord מפה = context.read (pathList.get (0)); כותרת מחרוזת = dataRecord.get ("כותרת");

התהליך כולו מאומת על ידי לִטעוֹן ממשק API:

assertEquals ("Skyfall", כותרת);

6.5. סרטו האחרון של במאי

דוגמה זו תמחיש את הדרך להבין את הסרט האחרון שביים במאי בשם סם מנדס.

ראשית, רשימה של כל הסרטים שביים סם מנדס נוצר:

DocumentContext context = JsonPath.parse (jsonString); רשימה dataList = context.read ("$ [? (@. במאי == 'סם מנדס')]");

רשימה זו משמשת למיצוי תאריכי יציאה. תאריכים אלה יאוחסנו במערך ואז ימוינו:

רשימת dateList = ArrayList חדש (); עבור (פריט מפה: dataList) {Object date = item.get ("תאריך שחרור"); dateList.add (תאריך); } ארוך [] dateArray = dateList.toArray (ארוך ארוך [0]); Arrays.sort (dateArray);

ה lastestTime משתנה, שהוא האלמנט האחרון במערך הממוין, משמש בשילוב עם מְנַהֵל ערך השדה לקביעת ה- כותרת של הסרט המבוקש:

long latestTime = dateArray [dateArray.length - 1]; רשימה finalDataList = context.read ("$ [? (@ ['במאי'] == 'סם מנדס' && @ ['תאריך שחרור'] ==" + אחרון זמן + ")]"); כותרת מחרוזת = (מחרוזת) finalDataList.get (0) .get ("כותרת");

הקביעה הבאה הוכיחה שהכל עובד כמצופה:

assertEquals ("ספקטר", כותרת);

7. מסקנה

מדריך זה כיסה את התכונות הבסיסיות של Jayway JsonPath - כלי רב עוצמה לחצייה ולניתוח מסמכי JSON.

למרות שיש ל- JsonPath חסרונות מסוימים, כמו למשל היעדר מפעילים להגיע לצמתים הורים או אחים, זה יכול להיות שימושי מאוד בהרבה תרחישים.

היישום של כל הדוגמאות וקטעי הקוד ניתן למצוא ב פרויקט GitHub.


$config[zx-auto] not found$config[zx-overlay] not found