التعامل مع بنى JSON المتداخلة
حوّل JSON المتداخل بعمق إلى إطارات بيانات منظمة لإجراء التحليل
التعامل مع بنى JSON المتداخلة درس مجاني في R Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في R Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة R Academy 4 دروس في المجموع.
لماذا يمثل JSON المتداخل تحديًا
تعيد REST APIs غالبًا JSON متداخلًا بعمق، حيث قد يحتوي حقل واحد على مصفوفة من الكائنات التي تحتوي بدورها على كائنات أخرى. ويتطلب تسطيح هذه البنية وتحويلها إلى إطار بيانات منظم فهم كيفية عمل jsonlite وpurrr وtidyr معًا.
# Example nested JSON from a REST API:
json_str <- '{
"user": {
"id": 1,
"name": "Alice",
"orders": [
{"order_id": 101, "total": 59.99, "status": "shipped"},
{"order_id": 102, "total": 24.50, "status": "pending"}
]
}
}'
# The challenge: 'orders' is an array of objects inside 'user'
cat('Nested JSON loaded as string, length:', nchar(json_str))fromJSON() — التحليل الأساسي
تحول jsonlite::fromJSON() سلسلة JSON أو مسار ملف إلى كائنات R. ويصبح JSON البسيط المسطح قائمة أو إطار بيانات. أما JSON المتداخل فيصبح قائمة متداخلة؛ وتتحول مصفوفات الكائنات إلى إطارات بيانات مخزنة داخل أعمدة القوائم.
library(jsonlite)
# Parse flat JSON
flat_json <- '{"name": "Alice", "age": 30, "score": 95.5}'
result <- fromJSON(flat_json)
cat('Name:', result$name, '\n')
cat('Age: ', result$age, '\n')
# Parse an array of objects — becomes a data frame
array_json <- '[{"id":1,"val":10},{"id":2,"val":20},{"id":3,"val":30}]'
df <- fromJSON(array_json)
cat('Class:', class(df), '\n')
print(df)fromJSON() مع flatten = TRUE
تخبر الوسيطة flatten = TRUE الدالة fromJSON() بفك حزم إطارات البيانات المتداخلة بشكل تكراري وتحويلها إلى أعمدة ذات أسماء مفصولة بنقاط. ويناسب ذلك مستوى واحدًا من التداخل، كما أنه أسرع طريقة للتعامل مع JSON متوسط التداخل.
library(jsonlite)
json_str <- '[{
"id": 1,
"name": "Alice",
"address": {"city": "Berlin", "country": "Germany"}
},{
"id": 2,
"name": "Bob",
"address": {"city": "Paris", "country": "France"}
}]'
# Without flatten:
nested_df <- fromJSON(json_str, flatten = FALSE)
cat('address class:', class(nested_df$address), '\n')
# With flatten = TRUE:
flat_df <- fromJSON(json_str, flatten = TRUE)
cat('Columns:', names(flat_df), '\n')
print(flat_df)تحويل المصفوفات المتداخلة إلى أعمدة قوائم
عندما يحتوي حقل JSON على مصفوفة من الكائنات، تخزنها fromJSON() في صورة عمود قائمة داخل إطار البيانات؛ إذ تحتوي كل خلية على إطار بيانات. ويجب الوصول إلى هذه العناصر أو فك تداخلها صراحةً.
library(jsonlite)
json_str <- '[{
"user_id": 1,
"tags": ["R", "Python", "SQL"]
},{
"user_id": 2,
"tags": ["Java", "Kotlin"]
}]'
df <- fromJSON(json_str)
cat('tags column class:', class(df$tags), '\n')
# Access tags for user 1:
cat('User 1 tags:', df$tags[[1]], '\n')
cat('User 2 tags:', df$tags[[2]])purrr::map() — استخراج الحقول المتداخلة
تطبّق purrr::map() دالةً على كل عنصر في قائمة. وعندما يكون كل عنصر قائمة مسماة، أي كائن JSON محللًا، يمكنك تمرير سلسلة نصية لاستخراج حقل مسمى من كل عنصر؛ وهو بديل موجز عن الحلقة.
library(jsonlite)
library(purrr)
json_str <- '[{
"id": 1,
"meta": {"score": 88, "grade": "B"}
},{
"id": 2,
"meta": {"score": 95, "grade": "A"}
},{
"id": 3,
"meta": {"score": 72, "grade": "C"}
}]'
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# Extract 'score' from each record's 'meta' object
scores <- map_dbl(records, function(r) r$meta$score)
grades <- map_chr(records, function(r) r$meta$grade)
cat('Scores:', scores, '\n')
cat('Grades:', grades)purrr::map() باستخدام الاختصار النصي
تمثل purrr::map(list, 'field_name') اختصارًا لاستخراج حقل مسمى من كل عنصر، وهي مكافئة لـ map(list, function(x) x[['field_name']]). استخدم map_chr() أو map_dbl() وغيرهما للحصول على متجهات ذرية ذات نوع محدد بدلًا من القوائم.
library(jsonlite)
library(purrr)
json_str <- '[{"name":"Alice","score":90},{"name":"Bob","score":78},{"name":"Carol","score":85}]'
# Parse as list of lists
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# String shortcut to extract field
names_vec <- map_chr(records, 'name')
scores_vec <- map_dbl(records, 'score')
cat('Names: ', names_vec, '\n')
cat('Scores:', scores_vec, '\n')
# Build a clean data frame
clean_df <- data.frame(name = names_vec, score = scores_vec)
print(clean_df)التداخل العميق باستخدام سلاسل map()
بالنسبة إلى JSON ذي التداخل العميق، اربط عدة استدعاءات لـ map() في سلسلة. ينزل كل استدعاء مستوىً واحدًا. استخدم map(list, 'field') في كل مستوى، وطبّق map_*() ذات النوع المحدد في الخطوة الأعمق لاستخراج القيمة النهائية.
library(jsonlite)
library(purrr)
json_str <- '[{
"id": 1,
"company": {"hq": {"city": "Berlin", "country": "Germany"}}
},{
"id": 2,
"company": {"hq": {"city": "Tokyo", "country": "Japan"}}
}]'
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# Navigate: records -> company -> hq -> city
cities <- map_chr(records, function(r) r$company$hq$city)
cat('Cities:', cities, '\n')
# Or using nested map shortcut:
ids <- map_int(records, 'id')
cat('IDs:', ids)tidyr::unnest() — تسطيح أعمدة القوائم
توسّع tidyr::unnest() عمود قائمة يحتوي على إطارات بيانات، فتنشئ صفًا لكل عنصر متداخل. وهذا هو الأسلوب المنظم القياسي لتسطيح العلاقات واحد إلى متعدد في بيانات JSON.
library(jsonlite)
library(tidyr)
library(dplyr)
json_str <- '[{
"user_id": 1,
"orders": [{"oid":101,"total":50},{"oid":102,"total":30}]
},{
"user_id": 2,
"orders": [{"oid":103,"total":80}]
}]'
df <- fromJSON(json_str)
cat('Before unnest, rows:', nrow(df), '\n')
cat('orders class:', class(df$orders), '\n')
# Unnest expands one row per order
expanded <- unnest(df, cols = orders)
cat('After unnest, rows:', nrow(expanded), '\n')
print(expanded)jsonlite::flatten() على إطارات البيانات
تعمل jsonlite::flatten() على إطار بيانات محلل مسبقًا، وليس على سلسلة JSON، فتوسّع بشكل تكراري أي أعمدة متداخلة لإطارات البيانات إلى أعمدة أسماء مفصولة بنقاط. وتفيد بعد fromJSON(..., flatten = FALSE) عندما تريد إجراء التسطيح كخطوة معالجة لاحقة.
library(jsonlite)
json_str <- '[{
"id": 1,
"profile": {"age": 25, "city": "Rome"}
},{
"id": 2,
"profile": {"age": 31, "city": "Oslo"}
}]'
# Parse without auto-flatten
nested <- fromJSON(json_str, flatten = FALSE)
cat('Columns before flatten:', names(nested), '\n')
cat('profile class:', class(nested$profile), '\n')
# Apply flatten() post-hoc
flat <- flatten(nested)
cat('Columns after flatten:', names(flat), '\n')
print(flat)التعامل مع القيم الفارغة في JSON المتداخل
تُحلل قيم JSON null إلى NULL في R، مما يسبب مشكلات عند إنشاء إطارات البيانات؛ إذ تؤدي قيمة NULL في قائمة إلى حذف العنصر بالكامل. استخدم purrr::map() مع الوسيطة .default أو استخدم %||% لاستبدال القيم المفقودة بأمان.
library(jsonlite)
library(purrr)
json_str <- '[{"id":1,"email":"alice@example.com"},{"id":2,"email":null},{"id":3,"email":"carol@example.com"}]'
records <- fromJSON(json_str, simplifyDataFrame = FALSE)
# Unsafe: NULL drops the element
# emails <- map_chr(records, 'email') # ERROR on null
# Safe: provide a default for missing values
emails <- map_chr(records, function(r) {
if (is.null(r$email)) NA_character_ else r$email
})
cat('Emails:', emails)
cat('NAs:', sum(is.na(emails)))خط أنابيب متكامل: من JSON الخاص بواجهة API إلى إطار بيانات منظم
بتجميع كل ما سبق، ننشئ خط أنابيب واقعيًا يحلل JSON متداخلًا من واجهة API، ويستخرج الحقول باستخدام purrr، ويتعامل مع القيم الفارغة، وينتج إطار بيانات منظمًا جاهزًا للتحليل.
library(jsonlite)
library(purrr)
library(dplyr)
# Simulated API response
api_json <- '[{
"id": 1, "name": "Alice",
"stats": {"score": 92, "rank": 1}
},{
"id": 2, "name": "Bob",
"stats": null
},{
"id": 3, "name": "Carol",
"stats": {"score": 85, "rank": 3}
}]'
records <- fromJSON(api_json, simplifyDataFrame = FALSE)
result <- tibble(
id = map_int(records, 'id'),
name = map_chr(records, 'name'),
score = map_dbl(records, function(r) if (is.null(r$stats)) NA_real_ else r$stats$score),
rank = map_int(records, function(r) if (is.null(r$stats)) NA_integer_ else r$stats$rank)
)
print(result)اختبار سريع
لديك إطار بيانات df يحتوي العمود orders على عمود قائمة يضم إطارات بيانات، إطارًا لكل مستخدم. ما الدالة التي توسّع هذا العمود إلى صف واحد لكل طلب؟
أهم الاستنتاجات من JSON المتداخل
يتطلب التعامل مع JSON المتداخل في R مجموعة أدوات متعددة المستويات:
fromJSON(json, flatten = TRUE)— تسطيح مستوى واحد من التداخل تلقائيًاfromJSON(json, simplifyDataFrame = FALSE)— الحصول على قائمة من القوائم لمعالجتها يدويًا- المصفوفات المتداخلة ← أعمدة قوائم في إطار البيانات الناتج
purrr::map_chr/dbl/int(list, 'field')— استخراج قيم ذات أنواع محددة من كل عنصر- تتعمق الاستدعاءات المتسلسلة لـ
map()عبر مستويات التداخل العميقة tidyr::unnest(df, cols = col)— توسيع أعمدة القوائم التي تحتوي على إطارات بياناتjsonlite::flatten(df)— تسطيح أعمدة إطارات البيانات المتداخلة بعد التحليل- احرص دائمًا على التعامل مع
NULLباستخدامif (is.null(x)) NA else x
library(jsonlite)
library(purrr)
# Quick reference:
json <- '[{"id":1,"info":{"val":10}},{"id":2,"info":null}]'
recs <- fromJSON(json, simplifyDataFrame = FALSE)
# Safe extraction with null guard
vals <- map_dbl(recs, function(r) {
if (is.null(r$info)) NA_real_ else r$info$val
})
result <- data.frame(id = map_int(recs, 'id'), val = vals)
print(result)الأسئلة الشائعة
هل درس «التعامل مع بنى JSON المتداخلة» مجاني؟
نعم — نص درس «التعامل مع بنى JSON المتداخلة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة R Academy، انتقل إلى CoddyKit PRO. تتضمن دورة R Academy 4 دروس في المجموع.
ماذا ستتعلم في «التعامل مع بنى JSON المتداخلة»؟
حوّل JSON المتداخل بعمق إلى إطارات بيانات منظمة لإجراء التحليل تتمرن على R Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ R Academy؟
لا تُشترط خبرة سابقة. R Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «التعامل مع بنى JSON المتداخلة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس R Academy هذا؟
نعم. كل درس في R Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تحليل JSON باستخدام jsonlite
- إجراء طلبات HTTP باستخدام httr2
- استهلاك REST APIs في R
- التعامل مع بنى JSON المتداخلة