Java DSL لقراءة مستندات JSON.
Jayway JsonPath هو منفذ Java لتنفيذ Stefan Goessner JsonPath.
JsonPath متاح في مستودع Maven المركزي. يضيف مستخدمو Maven هذا إلى POM الخاص بك.
< dependency >
< groupId >com.jayway.jsonpath</ groupId >
< artifactId >json-path</ artifactId >
< version >2.9.0</ version >
</ dependency >
إذا كنت بحاجة إلى مساعدة، فاطرح الأسئلة على Stack Overflow. ضع علامة على السؤال "jsonpath" و"Java".
تشير تعبيرات JsonPath دائمًا إلى بنية JSON بنفس الطريقة التي يتم بها استخدام تعبير XPath مع مستند XML. يُشار دائمًا إلى "كائن العضو الجذر" في JsonPath باسم $
بغض النظر عما إذا كان كائنًا أو صفيفًا.
يمكن لتعبيرات JsonPath استخدام تدوين النقطة
$.store.book[0].title
أو قوس - التدوين
$['store']['book'][0]['title']
المشغل | وصف |
---|---|
$ | العنصر الجذر للاستعلام. يؤدي هذا إلى بدء كافة تعبيرات المسار. |
@ | العقدة الحالية التي تتم معالجتها بواسطة مسند عامل التصفية. |
* | حرف البدل. متاح في أي مكان مطلوب اسم أو رقم رقمي. |
.. | المسح العميق. متاح في أي مكان مطلوب اسم. |
.<name> | طفل ذو علامة نقطية |
['<name>' (, '<name>')] | طفل أو أطفال مشار إليهم بين قوسين |
[<number> (, <number>)] | فهرس المصفوفة أو الفهارس |
[start:end] | مشغل شريحة المصفوفة |
[?(<expression>)] | تعبير التصفية. يجب تقييم التعبير إلى قيمة منطقية. |
يمكن استدعاء الوظائف في نهاية المسار - الإدخال إلى دالة هو إخراج تعبير المسار. يتم تحديد إخراج الوظيفة بواسطة الوظيفة نفسها.
وظيفة | وصف | نوع الإخراج |
---|---|---|
min() | يوفر القيمة الدنيا لمجموعة من الأرقام | مزدوج |
max() | يوفر القيمة القصوى لمجموعة من الأرقام | مزدوج |
avg() | يوفر القيمة المتوسطة لمجموعة من الأرقام | مزدوج |
stddev() | يوفر قيمة الانحراف المعياري لمجموعة من الأرقام | مزدوج |
length() | يوفر طول المصفوفة | عدد صحيح |
sum() | يوفر القيمة الإجمالية لمجموعة من الأرقام | مزدوج |
keys() | يوفر مفاتيح الخصائص (بديل للتيلدة الطرفية ~ ) | Set<E> |
concat(X) | يوفر نسخة متسلسلة من إخراج المسار مع عنصر جديد | مثل الإدخال |
append(X) | إضافة عنصر إلى صفيف إخراج مسار json | مثل الإدخال |
first() | يوفر العنصر الأول من مجموعة | يعتمد على المصفوفة |
last() | يوفر العنصر الأخير في المصفوفة | يعتمد على المصفوفة |
index(X) | يوفر عنصر مصفوفة الفهرس: X، إذا كانت X سالبة، فأخذها من الخلف | يعتمد على المصفوفة |
المرشحات هي تعبيرات منطقية تستخدم لتصفية المصفوفات. سيكون عامل التصفية النموذجي هو [?(@.age > 18)]
حيث يمثل @
العنصر الحالي الذي تتم معالجته. يمكن إنشاء مرشحات أكثر تعقيدًا باستخدام العوامل المنطقية &&
و ||
. يجب أن تكون السلسلة الحرفية محاطة بعلامات اقتباس مفردة أو مزدوجة ( [?(@.color == 'blue')]
أو [?(@.color == "blue")]
).
المشغل | وصف |
---|---|
== | اليسار يساوي اليمين (لاحظ أن 1 لا يساوي '1') |
!= | اليسار لا يساوي اليمين |
< | اليسار أقل من اليمين |
<= | اليسار أقل أو يساوي اليمين |
> | اليسار أكبر من اليمين |
>= | اليسار أكبر من أو يساوي اليمين |
=~ | اليسار يطابق التعبير العادي [?(@.name =~ /foo.*?/i)] |
in | اليسار موجود في اليمين [?(@.size in ['S', 'M'])] |
nin | اليسار غير موجود في اليمين |
subsetof | اليسار هو مجموعة فرعية من اليمين [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | اليسار لديه تقاطع مع اليمين [?(@.sizes Anyof ['M', 'L'])] |
noneof | ليس لليسار تقاطع مع اليمين [?(@.sizes noneof ['M', 'L'])] |
size | يجب أن يتطابق حجم اليسار (صفيف أو سلسلة) مع اليمين |
empty | يجب أن يكون اليسار (صفيف أو سلسلة) فارغًا |
بالنظر إلى json
{
"store" : {
"book" : [
{
"category" : "reference" ,
"author" : "Nigel Rees" ,
"title" : "Sayings of the Century" ,
"price" : 8.95
} ,
{
"category" : "fiction" ,
"author" : "Evelyn Waugh" ,
"title" : "Sword of Honour" ,
"price" : 12.99
} ,
{
"category" : "fiction" ,
"author" : "Herman Melville" ,
"title" : "Moby Dick" ,
"isbn" : "0-553-21311-3" ,
"price" : 8.99
} ,
{
"category" : "fiction" ,
"author" : "J. R. R. Tolkien" ,
"title" : "The Lord of the Rings" ,
"isbn" : "0-395-19395-8" ,
"price" : 22.99
}
] ,
"bicycle" : {
"color" : "red" ,
"price" : 19.95
}
} ,
"expensive" : 10
}
JsonPath | نتيجة |
---|---|
$.store.book[*].author | مؤلفي جميع الكتب |
$..author | جميع المؤلفين |
$.store.* | كل الأشياء، سواء الكتب أو الدراجات |
$.store..price | سعر كل شيء |
$..book[2] | الكتاب الثالث |
$..book[-2] | الكتاب الثاني إلى الأخير |
$..book[0,1] | الكتابين الأولين |
$..book[:2] | جميع الكتب من الفهرس 0 (شاملاً) حتى الفهرس 2 (حصرياً) |
$..book[1:2] | جميع الكتب من الفهرس 1 (شاملا) حتى الفهرس 2 (حصريا) |
$..book[-2:] | الكتابين الأخيرين |
$..book[2:] | جميع الكتب من الفهرس 2 (الشامل) إلى الأخير |
$..book[?(@.isbn)] | جميع الكتب ذات رقم ISBN |
$.store.book[?(@.price < 10)] | جميع الكتب في المتجر أرخص من 10 |
$..book[?(@.price <= $['expensive'])] | جميع الكتب الموجودة في المتجر "غير باهظة الثمن" |
$..book[?(@.author =~ /.*REES/i)] | جميع الكتب التي تطابق التعبير العادي (تجاهل حالة الأحرف) |
$..* | أعطني كل شيء |
$..book.length() | عدد الكتب |
إن أبسط طريقة مباشرة لاستخدام JsonPath هي عبر واجهة برمجة التطبيقات للقراءة الثابتة.
String json = "..." ;
List < String > authors = JsonPath . read ( json , "$.store.book[*].author" );
إذا كنت تريد القراءة مرة واحدة فقط، فلا بأس بذلك. في حال كنت بحاجة إلى قراءة مسار آخر أيضًا، فهذه ليست الطريقة الصحيحة حيث سيتم تحليل المستند في كل مرة تتصل فيها بـ JsonPath.read(...). لتجنب المشكلة يمكنك تحليل json أولاً.
String json = "..." ;
Object document = Configuration . defaultConfiguration (). jsonProvider (). parse ( json );
String author0 = JsonPath . read ( document , "$.store.book[0].author" );
String author1 = JsonPath . read ( document , "$.store.book[1].author" );
يوفر JsonPath أيضًا واجهة برمجة تطبيقات (API) بطلاقة. وهذا أيضًا هو الأكثر مرونة.
String json = "..." ;
ReadContext ctx = JsonPath . parse ( json );
List < String > authorsOfBooksWithISBN = ctx . read ( "$.store.book[?(@.isbn)].author" );
List < Map < String , Object >> expensiveBooks = JsonPath
. using ( configuration )
. parse ( json )
. read ( "$.store.book[?(@.price > 10)]" , List . class );
عند استخدام JsonPath في جافا، من المهم معرفة النوع الذي تتوقعه في النتيجة. سيحاول JsonPath تلقائيًا إرسال النتيجة إلى النوع الذي يتوقعه المستدعي.
//Will throw an java.lang.ClassCastException
List < String > list = JsonPath . parse ( json ). read ( "$.store.book[0].author" );
//Works fine
String author = JsonPath . parse ( json ). read ( "$.store.book[0].author" );
عند تقييم المسار، عليك أن تفهم مفهوم متى يكون المسار definite
. يكون المسار indefinite
إذا كان يحتوي على:
..
- عامل المسح العميق?(<expression>)
- تعبير[<number>, <number> (, <number>)]
- فهارس مصفوفة متعددة تُرجع المسارات Indefinite
دائمًا قائمة (كما يمثلها JsonProvider الحالي).
افتراضيًا، يتم توفير مخطط كائن بسيط بواسطة MappingProvider SPI. يتيح لك هذا تحديد نوع الإرجاع الذي تريده وسيحاول MappingProvider إجراء التعيين. في المثال أدناه، يتم توضيح التعيين بين Long
و Date
.
String json = "{ " date_as_long " : 1411455611975}" ;
Date date = JsonPath . parse ( json ). read ( "$['date_as_long']" , Date . class );
إذا قمت بتكوين JsonPath لاستخدام JacksonMappingProvider
أو GsonMappingProvider
أو JakartaJsonProvider
فيمكنك أيضًا تعيين مخرجات JsonPath مباشرة إلى POJO.
Book book = JsonPath . parse ( json ). read ( "$.store.book[0]" , Book . class );
للحصول على معلومات كاملة عن النوع العام، استخدم TypeRef.
TypeRef < List < String >> typeRef = new TypeRef < List < String >>() {};
List < String > titles = JsonPath . parse ( JSON_DOCUMENT ). read ( "$.store.book[*].title" , typeRef );
هناك ثلاث طرق مختلفة لإنشاء مسندات عامل التصفية في JsonPath.
المسندات المضمنة هي تلك المحددة في المسار.
List < Map < String , Object >> books = JsonPath . parse ( json )
. read ( "$.store.book[?(@.price < 10)]" );
يمكنك استخدام &&
و ||
لدمج مسندات متعددة [?(@.price < 10 && @.category == 'fiction')]
, [?(@.category == 'reference' || @.price > 10)]
.
يمكنك استخدام !
لنفي المسند [?(!(@.price < 10 && @.category == 'fiction'))]
.
يمكن إنشاء المسندات باستخدام Filter API كما هو موضح أدناه:
import static com . jayway . jsonpath . JsonPath . parse ;
import static com . jayway . jsonpath . Criteria . where ;
import static com . jayway . jsonpath . Filter . filter ;
...
...
Filter cheapFictionFilter = filter (
where ( "category" ). is ( "fiction" ). and ( "price" ). lte ( 10D )
);
List < Map < String , Object >> books =
parse ( json ). read ( "$.store.book[?]" , cheapFictionFilter );
لاحظ العنصر النائب ?
للمرشح في المسار. عند توفير عوامل تصفية متعددة، يتم تطبيقها بالترتيب الذي يجب أن يتطابق فيه عدد العناصر النائبة مع عدد عوامل التصفية المتوفرة. يمكنك تحديد عدة عناصر نائبة للمسند في عملية تصفية واحدة [?, ?]
، ويجب أن يتطابق كلا المسندين.
يمكن أيضًا دمج المرشحات مع "OR" و"AND"
Filter fooOrBar = filter (
where ( "foo" ). exists ( true )). or ( where ( "bar" ). exists ( true )
);
Filter fooAndBar = filter (
where ( "foo" ). exists ( true )). and ( where ( "bar" ). exists ( true )
);
الخيار الثالث هو تنفيذ المسندات الخاصة بك
Predicate booksWithISBN = new Predicate () {
@ Override
public boolean apply ( PredicateContext ctx ) {
return ctx . item ( Map . class ). containsKey ( "isbn" );
}
};
List < Map < String , Object >> books =
reader . read ( "$.store.book[?].isbn" , List . class , booksWithISBN );
في تطبيق Goessner، يمكن لـ JsonPath إرجاع Path
أو Value
. Value
هي القيمة الافتراضية وما ترجعه جميع الأمثلة المذكورة أعلاه. إذا كنت تفضل الحصول على مسار العناصر التي يصل إليها استعلامنا، فيمكن تحقيق ذلك باستخدام خيار.
Configuration conf = Configuration . builder ()
. options ( Option . AS_PATH_LIST ). build ();
List < String > pathList = using ( conf ). parse ( json ). read ( "$..author" );
assertThat ( pathList ). containsExactly (
"$['store']['book'][0]['author']" ,
"$['store']['book'][1]['author']" ,
"$['store']['book'][2]['author']" ,
"$['store']['book'][3]['author']" );
توفر المكتبة إمكانية تحديد القيمة.
String newJson = JsonPath . parse ( json ). set ( "$['store']['book'][0]['author']" , "Paul" ). jsonString ();
عند إنشاء التكوين الخاص بك، توجد بعض علامات الخيارات التي يمكنها تغيير السلوك الافتراضي.
DEFAULT_PATH_LEAF_TO_NULL
هذا الخيار يجعل JsonPath يُرجع قيمة فارغة للأوراق المفقودة. النظر في json التالي
[
{
"name" : "john" ,
"gender" : "male"
} ,
{
"name" : "ben"
}
]
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
String gender0 = JsonPath . using ( conf ). parse ( json ). read ( "$[0]['gender']" );
//PathNotFoundException thrown
String gender1 = JsonPath . using ( conf ). parse ( json ). read ( "$[1]['gender']" );
Configuration conf2 = conf . addOptions ( Option . DEFAULT_PATH_LEAF_TO_NULL );
//Works fine
String gender0 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[0]['gender']" );
//Works fine (null is returned)
String gender1 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[1]['gender']" );
ALWAYS_RETURN_LIST
يقوم هذا الخيار بتكوين JsonPath لإرجاع قائمة حتى عندما يكون المسار definite
.
Configuration conf = Configuration . defaultConfiguration ();
//ClassCastException thrown
List < String > genders0 = JsonPath . using ( conf ). parse ( json ). read ( "$[0]['gender']" );
Configuration conf2 = conf . addOptions ( Option . ALWAYS_RETURN_LIST );
//Works fine
List < String > genders0 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[0]['gender']" );
SUPPRESS_EXCEPTIONS
يتأكد هذا الخيار من عدم نشر أي استثناءات من تقييم المسار. ويتبع هذه القواعد البسيطة:
ALWAYS_RETURN_LIST
موجودًا، فسيتم إرجاع قائمة فارغةALWAYS_RETURN_LIST
غير موجود، فسيتم إرجاعه فارغًا REQUIRE_PROPERTIES
يقوم هذا الخيار بتكوين JsonPath ليطلب الخصائص المحددة في المسار عند تقييم مسار indefinite
.
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
List < String > genders = JsonPath . using ( conf ). parse ( json ). read ( "$[*]['gender']" );
Configuration conf2 = conf . addOptions ( Option . REQUIRE_PROPERTIES );
//PathNotFoundException thrown
List < String > genders = JsonPath . using ( conf2 ). parse ( json ). read ( "$[*]['gender']" );
يتم شحن JsonPath مع خمسة JsonProviders مختلفين:
يجب أن يتم تغيير إعدادات التكوين الافتراضية كما هو موضح فقط عند تهيئة التطبيق الخاص بك. لا يُنصح بشدة بإجراء تغييرات أثناء وقت التشغيل، خاصة في التطبيقات متعددة الخيوط.
Configuration . setDefaults ( new Configuration . Defaults () {
private final JsonProvider jsonProvider = new JacksonJsonProvider ();
private final MappingProvider mappingProvider = new JacksonMappingProvider ();
@ Override
public JsonProvider jsonProvider () {
return jsonProvider ;
}
@ Override
public MappingProvider mappingProvider () {
return mappingProvider ;
}
@ Override
public Set < Option > options () {
return EnumSet . noneOf ( Option . class );
}
});
لاحظ أن JacksonJsonProvider يتطلب com.fasterxml.jackson.core:jackson-databind:2.4.5
وGsonJsonProvider يتطلب com.google.code.gson:gson:2.3.1
على مسار الفصل الخاص بك.
يتوقع كل من موفري Jakarta EE 9 JSON-P (JSR-342) وJSON-B (JSR-367) Java 8 على الأقل ويتطلبون تطبيقات JSON API متوافقة (مثل Eclipse Glassfish وEclipse Yasson) في مسار فئة وقت تشغيل التطبيق؛ يمكن أيضًا توفير مثل هذه التطبيقات بواسطة حاوية تطبيق Java EE. يرجى أيضًا ملاحظة أن Apache Johnzon غير متوافق مع classpath مع مواصفات Jakarta EE 9 حتى الآن، وإذا تم اختيار موفر تعيين JSON-B، فيجب تكوين موفر JSON-P واستخدامه أيضًا.
إحدى ميزات جاكرتا EE 9 لمعالجة JSON وربط البيانات (رسم الخرائط) هي عدم قابلية تغيير مصفوفات Json والكائنات بمجرد تحليلها بالكامل أو كتابتها. لاحترام مواصفات واجهة برمجة التطبيقات (API)، مع السماح لـ JsonPath بتعديل مستندات Json من خلال عمليات الإضافة، والضبط/الوضع، والاستبدال، والحذف، يجب تهيئة JakartaJsonProvider
باستخدام وسيطة true
اختيارية:
JsonProvider jsonProvider = new JakartaJsonProvider(true)
(تمكين صفائف وكائنات Json القابلة للتغيير)JsonProvider jsonProvider = new JakartaJsonProvider()
(افتراضي، امتثال صارم لواجهة برمجة تطبيقات JSON-P)يتم دعم كافة عمليات البحث والقراءة باستخدام JsonPath بغض النظر عن وضع التهيئة. يحتاج الوضع الافتراضي أيضًا إلى ذاكرة أقل ويكون أكثر أداءً.
في JsonPath 2.1.0 تم تقديم Cache SPI جديد. يتيح ذلك لمستهلكي واجهة برمجة التطبيقات (API) تكوين التخزين المؤقت للمسار بطريقة تناسب احتياجاتهم. يجب تكوين ذاكرة التخزين المؤقت قبل الوصول إليها لأول مرة أو يتم طرح JsonPathException. يأتي JsonPath مع تطبيقين لذاكرة التخزين المؤقت
com.jayway.jsonpath.spi.cache.LRUCache
(افتراضي، مؤشر ترابط آمن)com.jayway.jsonpath.spi.cache.NOOPCache
(بدون ذاكرة تخزين مؤقت)إذا كنت تريد تنفيذ ذاكرة التخزين المؤقت الخاصة بك، فإن واجهة برمجة التطبيقات (API) بسيطة.
CacheProvider . setCache ( new Cache () {
//Not thread safe simple cache
private Map < String , JsonPath > map = new HashMap < String , JsonPath >();
@ Override
public JsonPath get ( String key ) {
return map . get ( key );
}
@ Override
public void put ( String key , JsonPath jsonPath ) {
map . put ( key , jsonPath );
}
});