Как получить доступ к вложенным ключам в JSON-объекте Python

PythonBeginner
Практиковаться сейчас

Введение

Универсальность Python распространяется на работу с данными в формате JSON, широко используемом для хранения и обмена информацией. Структуры JSON могут быть простыми или сложными, с вложенными элементами, аналогично словарям и спискам Python. В этом руководстве вы узнаете, как получать доступ и извлекать данные из вложенных структур JSON в Python с помощью практических упражнений.

К концу этой лабораторной работы вы сможете уверенно ориентироваться в объектах JSON, получать доступ к глубоко вложенным ключам и работать с вложенными массивами в приложениях Python.

Понимание JSON в Python

JSON (JavaScript Object Notation) — это облегченный формат обмена данными, который легко читается человеком и обрабатывается машиной. В Python объекты JSON представлены как словари, а массивы JSON — как списки.

Давайте начнем с создания простого скрипта Python для изучения данных JSON:

  1. Откройте WebIDE и создайте новый файл, нажав «File > New File» в меню.

  2. Сохраните файл как basic_json.py в каталоге /home/labex/project/json_practice.

  3. Добавьте следующий код в basic_json.py:

import json

## A simple JSON object (represented as a Python dictionary)
person = {
    "name": "John Doe",
    "age": 30,
    "email": "john.doe@example.com",
    "is_employed": True,
    "hobbies": ["reading", "hiking", "coding"]
}

## Convert Python dictionary to JSON string
json_string = json.dumps(person, indent=2)
print("JSON as string:")
print(json_string)
print("\n" + "-" * 50 + "\n")

## Convert JSON string back to Python dictionary
parsed_json = json.loads(json_string)
print("Python dictionary:")
print(parsed_json)
print("\n" + "-" * 50 + "\n")

## Accessing basic elements
print("Basic access examples:")
print(f"Name: {parsed_json['name']}")
print(f"Age: {parsed_json['age']}")
print(f"First hobby: {parsed_json['hobbies'][0]}")
  1. Запустите скрипт, открыв терминал в WebIDE и выполнив:
cd /home/labex/project/json_practice
python3 basic_json.py

Вы должны увидеть следующий вывод:

JSON as string:
{
  "name": "John Doe",
  "age": 30,
  "email": "john.doe@example.com",
  "is_employed": true,
  "hobbies": [
    "reading",
    "hiking",
    "coding"
  ]
}

--------------------------------------------------

Python dictionary:
{'name': 'John Doe', 'age': 30, 'email': 'john.doe@example.com', 'is_employed': True, 'hobbies': ['reading', 'hiking', 'coding']}

--------------------------------------------------

Basic access examples:
Name: John Doe
Age: 30
First hobby: reading

Понимание кода

  • json.dumps() преобразует объект Python в строку в формате JSON
  • json.loads() разбирает строку JSON и преобразует ее в объект Python
  • Для доступа к элементам в объекте JSON используйте синтаксис словаря: object_name['key']
  • Для доступа к элементам в массиве JSON используйте индексацию списка: array_name[index]

Этот пример демонстрирует основные операции для работы с JSON в Python. На следующем шаге мы рассмотрим, как получить доступ к вложенным структурам JSON.

Доступ к вложенным ключам словаря

Объекты JSON часто содержат вложенные структуры. В Python мы можем получить доступ к вложенным данным, используя несколько квадратных скобок или объединяя ключи словаря.

Давайте создадим новый скрипт для изучения вложенных объектов JSON:

  1. В WebIDE создайте новый файл и сохраните его как nested_dict.py в каталоге /home/labex/project/json_practice.

  2. Добавьте следующий код в nested_dict.py:

import json

## JSON with nested object
user_data = {
    "name": "John Doe",
    "age": 30,
    "contact": {
        "email": "john.doe@example.com",
        "phone": "555-1234",
        "address": {
            "street": "123 Main St",
            "city": "Anytown",
            "state": "CA",
            "zip": "12345"
        }
    },
    "preferences": {
        "theme": "dark",
        "notifications": True
    }
}

## Let's prettify and print the JSON structure
print("Full JSON structure:")
print(json.dumps(user_data, indent=2))
print("\n" + "-" * 50 + "\n")

## Accessing nested elements
print("Accessing nested elements:")
print(f"Email: {user_data['contact']['email']}")
print(f"City: {user_data['contact']['address']['city']}")
print(f"Theme: {user_data['preferences']['theme']}")
print("\n" + "-" * 50 + "\n")

## Safe access with get() method
print("Safe access with get():")
## get() returns None if key doesn't exist, or a default value if specified
phone = user_data.get('contact', {}).get('phone', 'Not available')
country = user_data.get('contact', {}).get('address', {}).get('country', 'Not specified')

print(f"Phone: {phone}")
print(f"Country: {country}")  ## This key doesn't exist but won't cause an error
  1. Запустите скрипт в терминале:
cd /home/labex/project/json_practice
python3 nested_dict.py

Вы должны увидеть вывод, похожий на следующий:

Full JSON structure:
{
  "name": "John Doe",
  "age": 30,
  "contact": {
    "email": "john.doe@example.com",
    "phone": "555-1234",
    "address": {
      "street": "123 Main St",
      "city": "Anytown",
      "state": "CA",
      "zip": "12345"
    }
  },
  "preferences": {
    "theme": "dark",
    "notifications": true
  }
}

--------------------------------------------------

Accessing nested elements:
Email: john.doe@example.com
City: Anytown
Theme: dark

--------------------------------------------------

Safe access with get():
Phone: 555-1234
Country: Not specified

Понимание вложенного доступа

При работе с вложенными структурами JSON вы можете получить доступ к вложенным элементам, объединяя ключи с помощью квадратных скобок:

## Syntax for nested access
value = json_data['key1']['key2']['key3']

Однако этот подход может вызвать ошибки, если какой-либо ключ в цепочке не существует. Более безопасный метод — использовать функцию get(), которая позволяет предоставить значение по умолчанию, если ключ отсутствует:

## Safe access with get() method
value = json_data.get('key1', {}).get('key2', {}).get('key3', 'default_value')

Этот безопасный шаблон доступа особенно ценен при работе с ответами API или другими внешними данными JSON, структура которых может быть непоследовательной.

Работа с вложенными массивами в JSON

Данные JSON часто включают массивы (списки в Python), которые могут содержать другие объекты или массивы. Давайте рассмотрим, как получить доступ к элементам внутри вложенных массивов.

  1. В WebIDE создайте новый файл и сохраните его как nested_arrays.py в каталоге /home/labex/project/json_practice.

  2. Добавьте следующий код в nested_arrays.py:

import json

## JSON with nested arrays
company_data = {
    "name": "Tech Innovations Inc",
    "founded": 2010,
    "departments": [
        {
            "name": "Engineering",
            "employees": [
                {"name": "Alice Johnson", "role": "Software Engineer", "skills": ["Python", "JavaScript", "AWS"]},
                {"name": "Bob Smith", "role": "DevOps Engineer", "skills": ["Docker", "Kubernetes", "Linux"]}
            ]
        },
        {
            "name": "Marketing",
            "employees": [
                {"name": "Carol Williams", "role": "Marketing Manager", "skills": ["SEO", "Content Strategy"]},
                {"name": "Dave Brown", "role": "Social Media Specialist", "skills": ["Facebook Ads", "Instagram"]}
            ]
        }
    ],
    "locations": ["San Francisco", "New York", "London"]
}

## Print the JSON structure
print("Company Data:")
print(json.dumps(company_data, indent=2))
print("\n" + "-" * 50 + "\n")

## Accessing elements in arrays
print("Accessing array elements:")
print(f"First location: {company_data['locations'][0]}")
print(f"Number of departments: {len(company_data['departments'])}")
print(f"First department name: {company_data['departments'][0]['name']}")
print("\n" + "-" * 50 + "\n")

## Iterating through nested arrays
print("All employees and their skills:")
for department in company_data['departments']:
    dept_name = department['name']
    print(f"\nDepartment: {dept_name}")
    print("-" * 20)

    for employee in department['employees']:
        print(f"  {employee['name']} ({employee['role']})")
        print(f"  Skills: {', '.join(employee['skills'])}")
        print()

## Finding specific data in nested arrays
print("-" * 50 + "\n")
print("Finding employees with Python skills:")

for department in company_data['departments']:
    for employee in department['employees']:
        if "Python" in employee['skills']:
            print(f"  {employee['name']} in {department['name']} department")
  1. Запустите скрипт в терминале:
cd /home/labex/project/json_practice
python3 nested_arrays.py

Вы должны увидеть вывод, похожий на следующий:

Company Data:
{
  "name": "Tech Innovations Inc",
  "founded": 2010,
  "departments": [
    {
      "name": "Engineering",
      "employees": [
        {
          "name": "Alice Johnson",
          "role": "Software Engineer",
          "skills": [
            "Python",
            "JavaScript",
            "AWS"
          ]
        },
        {
          "name": "Bob Smith",
          "role": "DevOps Engineer",
          "skills": [
            "Docker",
            "Kubernetes",
            "Linux"
          ]
        }
      ]
    },
    {
      "name": "Marketing",
      "employees": [
        {
          "name": "Carol Williams",
          "role": "Marketing Manager",
          "skills": [
            "SEO",
            "Content Strategy"
          ]
        },
        {
          "name": "Dave Brown",
          "role": "Social Media Specialist",
          "skills": [
            "Facebook Ads",
            "Instagram"
          ]
        }
      ]
    }
  ],
  "locations": [
    "San Francisco",
    "New York",
    "London"
  ]
}

--------------------------------------------------

Accessing array elements:
First location: San Francisco
Number of departments: 2
First department name: Engineering

--------------------------------------------------

All employees and their skills:

Department: Engineering
--------------------
  Alice Johnson (Software Engineer)
  Skills: Python, JavaScript, AWS

  Bob Smith (DevOps Engineer)
  Skills: Docker, Kubernetes, Linux


Department: Marketing
--------------------
  Carol Williams (Marketing Manager)
  Skills: SEO, Content Strategy

  Dave Brown (Social Media Specialist)
  Skills: Facebook Ads, Instagram

--------------------------------------------------

Finding employees with Python skills:
  Alice Johnson in Engineering department

Понимание доступа к вложенным массивам

Работа с вложенными массивами в JSON включает в себя комбинацию:

  1. Индексированный доступ: Используйте квадратные скобки с индексом для доступа к определенным элементам массива (array[0])
  2. Доступ к вложенным свойствам: Объединяйте ключи словаря и индексы массива (data['departments'][0]['employees'])
  3. Итерация: Используйте циклы для обработки нескольких элементов в массивах

Наиболее распространенный шаблон для работы с вложенными массивами — использование вложенных циклов for:

for outer_item in json_data['outer_array']:
    for inner_item in outer_item['inner_array']:
        ## Process the inner_item
        print(inner_item['property'])

Этот подход позволяет вам обходить сложные вложенные структуры и извлекать конкретные данные, которые вам нужны.

Обработка отсутствующих ключей и предотвращение ошибок

При работе со сложными структурами JSON, особенно из внешних источников, таких как API, важно обрабатывать потенциальные ошибки, возникающие при отсутствии ключей. Давайте рассмотрим методы безопасного доступа к вложенным данным JSON.

  1. В WebIDE создайте новый файл и сохраните его как error_handling.py в каталоге /home/labex/project/json_practice.

  2. Добавьте следующий код в error_handling.py:

import json

## JSON with inconsistent structure
api_response = {
    "status": "success",
    "data": {
        "users": [
            {
                "id": 1,
                "name": "John Doe",
                "contact": {
                    "email": "john.doe@example.com",
                    "phone": "555-1234"
                },
                "roles": ["admin", "user"]
            },
            {
                "id": 2,
                "name": "Jane Smith",
                ## Missing contact info
                "roles": ["user"]
            },
            {
                "id": 3,
                "name": "Bob Johnson",
                "contact": {
                    ## Only has email, no phone
                    "email": "bob.johnson@example.com"
                }
                ## Missing roles
            }
        ]
    }
}

print("API Response Structure:")
print(json.dumps(api_response, indent=2))
print("\n" + "-" * 50 + "\n")

## Approach 1: Using try-except blocks
print("Method 1: Using try-except blocks")
print("-" * 30)

for user in api_response["data"]["users"]:
    print(f"User: {user['name']}")

    ## Get email
    try:
        email = user['contact']['email']
        print(f"  Email: {email}")
    except (KeyError, TypeError):
        print("  Email: Not available")

    ## Get phone
    try:
        phone = user['contact']['phone']
        print(f"  Phone: {phone}")
    except (KeyError, TypeError):
        print("  Phone: Not available")

    ## Get roles
    try:
        roles = ", ".join(user['roles'])
        print(f"  Roles: {roles}")
    except (KeyError, TypeError):
        print("  Roles: None assigned")

    print()

## Approach 2: Using get() method with defaults
print("\n" + "-" * 50 + "\n")
print("Method 2: Using get() method with defaults")
print("-" * 30)

for user in api_response["data"]["users"]:
    print(f"User: {user['name']}")

    ## Get contact info with nested get() calls
    contact = user.get('contact', {})
    email = contact.get('email', 'Not available')
    phone = contact.get('phone', 'Not available')

    print(f"  Email: {email}")
    print(f"  Phone: {phone}")

    ## Get roles with default empty list
    roles = user.get('roles', [])
    roles_str = ", ".join(roles) if roles else "None assigned"
    print(f"  Roles: {roles_str}")

    print()
  1. Запустите скрипт в терминале:
cd /home/labex/project/json_practice
python3 error_handling.py

Вы должны увидеть вывод, похожий на следующий:

API Response Structure:
{
  "status": "success",
  "data": {
    "users": [
      {
        "id": 1,
        "name": "John Doe",
        "contact": {
          "email": "john.doe@example.com",
          "phone": "555-1234"
        },
        "roles": [
          "admin",
          "user"
        ]
      },
      {
        "id": 2,
        "name": "Jane Smith",
        "roles": [
          "user"
        ]
      },
      {
        "id": 3,
        "name": "Bob Johnson",
        "contact": {
          "email": "bob.johnson@example.com"
        }
      }
    ]
  }
}

--------------------------------------------------

Method 1: Using try-except blocks
------------------------------
User: John Doe
  Email: john.doe@example.com
  Phone: 555-1234
  Roles: admin, user

User: Jane Smith
  Email: Not available
  Phone: Not available
  Roles: user

User: Bob Johnson
  Email: bob.johnson@example.com
  Phone: Not available
  Roles: None assigned


--------------------------------------------------

Method 2: Using get() method with defaults
------------------------------
User: John Doe
  Email: john.doe@example.com
  Phone: 555-1234
  Roles: admin, user

User: Jane Smith
  Email: Not available
  Phone: Not available
  Roles: user

User: Bob Johnson
  Email: bob.johnson@example.com
  Phone: Not available
  Roles: None assigned

Понимание методов обработки ошибок

В примере демонстрируются два основных подхода к безопасному доступу к вложенным данным JSON:

  1. Блоки Try-Except

    • Оберните потенциально рискованный доступ в блоки try-except
    • Перехватывайте KeyError (отсутствующий ключ словаря) и TypeError (попытка доступа к ключу в не-словаре)
    • Предоставляйте резервные значения при возникновении ошибок
  2. Метод Chained get()

    • Используйте метод get() словаря, который принимает значение по умолчанию в качестве второго аргумента
    • Объединяйте несколько вызовов get() для вложенных структур
    • Обеспечивает более чистый код без обработки исключений

Метод get() обычно предпочтительнее из-за его читаемости и краткости при работе с вложенными структурами JSON. Он позволяет предоставлять значения по умолчанию на каждом уровне вложенности.

## Safe nested access pattern
value = data.get('level1', {}).get('level2', {}).get('level3', 'default_value')

Использование этих методов обработки ошибок сделает ваш код более надежным при работе с данными JSON из различных источников.

Практическое упражнение: Создание экстрактора данных JSON

Давайте применим ваши знания на практике, создав программу, которая извлекает конкретную информацию из сложной структуры JSON. Это может представлять собой реальный сценарий, когда вы получаете данные JSON из API и должны их обработать.

  1. В WebIDE создайте новый файл и сохраните его как json_extractor.py в каталоге /home/labex/project/json_practice.

  2. Добавьте следующий код в json_extractor.py:

import json

## A complex nested JSON structure (e.g., from a weather API)
weather_data = {
    "location": {
        "name": "New York",
        "region": "New York",
        "country": "United States of America",
        "lat": 40.71,
        "lon": -74.01,
        "timezone": "America/New_York"
    },
    "current": {
        "temp_c": 22.0,
        "temp_f": 71.6,
        "condition": {
            "text": "Partly cloudy",
            "icon": "//cdn.weatherapi.com/weather/64x64/day/116.png",
            "code": 1003
        },
        "wind_mph": 6.9,
        "wind_kph": 11.2,
        "wind_dir": "ENE",
        "humidity": 65,
        "cloud": 75,
        "feelslike_c": 22.0,
        "feelslike_f": 71.6
    },
    "forecast": {
        "forecastday": [
            {
                "date": "2023-09-20",
                "day": {
                    "maxtemp_c": 24.3,
                    "maxtemp_f": 75.7,
                    "mintemp_c": 18.6,
                    "mintemp_f": 65.5,
                    "condition": {
                        "text": "Patchy rain possible",
                        "icon": "//cdn.weatherapi.com/weather/64x64/day/176.png",
                        "code": 1063
                    },
                    "daily_chance_of_rain": 85
                },
                "astro": {
                    "sunrise": "06:41 AM",
                    "sunset": "07:01 PM",
                    "moonrise": "10:15 AM",
                    "moonset": "08:52 PM"
                },
                "hour": [
                    {
                        "time": "2023-09-20 00:00",
                        "temp_c": 20.1,
                        "condition": {
                            "text": "Clear",
                            "icon": "//cdn.weatherapi.com/weather/64x64/night/113.png",
                            "code": 1000
                        },
                        "chance_of_rain": 0
                    },
                    {
                        "time": "2023-09-20 12:00",
                        "temp_c": 23.9,
                        "condition": {
                            "text": "Overcast",
                            "icon": "//cdn.weatherapi.com/weather/64x64/day/122.png",
                            "code": 1009
                        },
                        "chance_of_rain": 20
                    }
                ]
            },
            {
                "date": "2023-09-21",
                "day": {
                    "maxtemp_c": 21.2,
                    "maxtemp_f": 70.2,
                    "mintemp_c": 16.7,
                    "mintemp_f": 62.1,
                    "condition": {
                        "text": "Heavy rain",
                        "icon": "//cdn.weatherapi.com/weather/64x64/day/308.png",
                        "code": 1195
                    },
                    "daily_chance_of_rain": 92
                },
                "astro": {
                    "sunrise": "06:42 AM",
                    "sunset": "06:59 PM",
                    "moonrise": "11:30 AM",
                    "moonset": "09:15 PM"
                }
            }
        ]
    }
}

def extract_weather_summary(data):
    """
    Extract and format a weather summary from the provided data.
    """
    try:
        ## Location information
        location = data.get("location", {})
        location_name = location.get("name", "Unknown")
        country = location.get("country", "Unknown")

        ## Current weather
        current = data.get("current", {})
        temp_c = current.get("temp_c", "N/A")
        temp_f = current.get("temp_f", "N/A")
        condition = current.get("condition", {}).get("text", "Unknown")
        humidity = current.get("humidity", "N/A")

        ## Forecast
        forecast_days = data.get("forecast", {}).get("forecastday", [])

        ## Build summary string
        summary = f"Weather Summary for {location_name}, {country}\n"
        summary += f"==================================================\n\n"
        summary += f"Current Conditions:\n"
        summary += f"  Temperature: {temp_c}°C ({temp_f}°F)\n"
        summary += f"  Condition: {condition}\n"
        summary += f"  Humidity: {humidity}%\n\n"

        if forecast_days:
            summary += "Forecast:\n"
            for day_data in forecast_days:
                date = day_data.get("date", "Unknown date")
                day = day_data.get("day", {})
                max_temp = day.get("maxtemp_c", "N/A")
                min_temp = day.get("mintemp_c", "N/A")
                condition = day.get("condition", {}).get("text", "Unknown")
                rain_chance = day.get("daily_chance_of_rain", "N/A")

                summary += f"  {date}:\n"
                summary += f"    High: {max_temp}°C, Low: {min_temp}°C\n"
                summary += f"    Condition: {condition}\n"
                summary += f"    Chance of Rain: {rain_chance}%\n"

                ## Get sunrise and sunset times if available
                astro = day_data.get("astro", {})
                if astro:
                    sunrise = astro.get("sunrise", "N/A")
                    sunset = astro.get("sunset", "N/A")
                    summary += f"    Sunrise: {sunrise}, Sunset: {sunset}\n"

                summary += "\n"

        return summary

    except Exception as e:
        return f"Error extracting weather data: {str(e)}"

## Print the full JSON data
print("Original Weather Data:")
print(json.dumps(weather_data, indent=2))
print("\n" + "-" * 60 + "\n")

## Extract and print the weather summary
weather_summary = extract_weather_summary(weather_data)
print(weather_summary)

## Save the summary to a file
with open("weather_summary.txt", "w") as file:
    file.write(weather_summary)

print("\nWeather summary has been saved to 'weather_summary.txt'")
  1. Запустите скрипт в терминале:
cd /home/labex/project/json_practice
python3 json_extractor.py
  1. После запуска скрипта вы можете просмотреть сгенерированный файл сводки:
cat weather_summary.txt

Вы увидите отформатированную сводку погоды, извлеченную из сложной структуры JSON.

Понимание экстрактора JSON

Это практическое упражнение демонстрирует несколько важных концепций для работы с вложенными данными JSON:

  1. Безопасное извлечение данных

    • Использование метода get() со значениями по умолчанию для обработки отсутствующих ключей
    • Доступ к вложенным данным с помощью цепочки вызовов get()
    • Обработка ошибок с помощью блоков try-except
  2. Преобразование данных

    • Преобразование данных JSON в удобочитаемый формат
    • Итерация по вложенным массивам для обработки нескольких элементов
    • Извлечение только релевантной информации из сложной структуры
  3. Защитное программирование

    • Предвидение и обработка потенциальных проблем
    • Предоставление значений по умолчанию при отсутствии данных
    • Перехват исключений для предотвращения сбоев программы

Этот шаблон извлечения, преобразования и представления данных JSON является общим во многих реальных приложениях, таких как:

  • Обработка ответов API
  • Создание отчетов из данных
  • Фильтрация и отображение информации для пользователей

Следуя этим шаблонам, вы можете надежно работать с данными JSON любой сложности.

Резюме

В этой лабораторной работе вы узнали, как работать с вложенными структурами JSON в Python:

  1. Базовая обработка JSON - Преобразование между объектами Python и строками JSON с использованием json.dumps() и json.loads().

  2. Доступ к вложенным ключам словаря - Использование цепочки квадратных скобок для доступа к глубоко вложенным значениям в объектах JSON.

  3. Работа с вложенными массивами - Навигация и извлечение данных из массивов внутри структур JSON с использованием индексации и итерации.

  4. Методы обработки ошибок - Реализация безопасных шаблонов доступа с блоками try-except и методом get() для обработки отсутствующих ключей.

  5. Практическое извлечение данных - Создание полноценного приложения, которое извлекает, преобразует и представляет данные из сложной структуры JSON.

Эти навыки необходимы для работы с данными из API, файлов конфигурации и других сценариев обмена данными. Теперь у вас есть основа для уверенной работы с данными JSON любой сложности в ваших приложениях Python.

Для дальнейшего изучения рассмотрите:

  • Работу с реальными API, возвращающими данные JSON
  • Использование библиотек, таких как pandas, для анализа данных JSON
  • Реализацию валидации JSON с помощью jsonschema
  • Создание и манипулирование пользовательскими структурами JSON