Como acessar chaves aninhadas em um objeto JSON Python

PythonBeginner
Pratique Agora

Introdução

A versatilidade do Python estende-se ao trabalho com dados JSON, um formato amplamente utilizado para armazenar e trocar informações. As estruturas JSON podem ser simples ou complexas, com elementos aninhados, semelhantes aos dicionários e listas do Python. Neste tutorial, você aprenderá como acessar e extrair dados de estruturas JSON aninhadas em Python através de exercícios práticos.

Ao final deste laboratório, você será capaz de navegar com confiança em objetos JSON, acessar chaves profundamente aninhadas e trabalhar com arrays aninhados em aplicações Python.

Compreendendo JSON em Python

JSON (JavaScript Object Notation) é um formato leve de intercâmbio de dados que é legível por humanos e analisável por máquinas. Em Python, objetos JSON são representados como dicionários, e arrays JSON como listas.

Vamos começar criando um script Python simples para explorar dados JSON:

  1. Abra o WebIDE e crie um novo arquivo clicando em "File > New File" no menu.

  2. Salve o arquivo como basic_json.py no diretório /home/labex/project/json_practice.

  3. Adicione o seguinte código a 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. Execute o script abrindo um terminal no WebIDE e executando:
cd /home/labex/project/json_practice
python3 basic_json.py

Você deve ver a seguinte saída:

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

Entendendo o Código

  • json.dumps() converte um objeto Python em uma string formatada em JSON
  • json.loads() analisa uma string JSON e a converte em um objeto Python
  • Para acessar elementos em um objeto JSON, use a sintaxe do dicionário: nome_do_objeto['chave']
  • Para acessar elementos em um array JSON, use a indexação de lista: nome_do_array[índice]

Este exemplo demonstra as operações básicas para trabalhar com JSON em Python. No próximo passo, exploraremos como acessar estruturas JSON aninhadas.

Acessando Chaves de Dicionários Aninhados

Objetos JSON frequentemente contêm estruturas aninhadas. Em Python, podemos acessar dados aninhados usando múltiplos colchetes ou encadeando chaves de dicionário.

Vamos criar um novo script para explorar objetos JSON aninhados:

  1. No WebIDE, crie um novo arquivo e salve-o como nested_dict.py no diretório /home/labex/project/json_practice.

  2. Adicione o seguinte código a 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. Execute o script no terminal:
cd /home/labex/project/json_practice
python3 nested_dict.py

Você deve ver uma saída semelhante a:

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

Compreendendo o Acesso Aninhado

Ao trabalhar com estruturas JSON aninhadas, você pode acessar elementos aninhados encadeando chaves com colchetes:

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

No entanto, essa abordagem pode causar erros se alguma chave na cadeia não existir. O método mais seguro é usar a função get(), que permite fornecer um valor padrão se uma chave estiver ausente:

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

Este padrão de acesso seguro é particularmente valioso ao trabalhar com respostas de API ou outros dados JSON externos, onde a estrutura pode não ser consistente.

Trabalhando com Arrays Aninhados em JSON

Dados JSON frequentemente incluem arrays (listas em Python) que podem conter outros objetos ou arrays. Vamos explorar como acessar elementos dentro de arrays aninhados.

  1. No WebIDE, crie um novo arquivo e salve-o como nested_arrays.py no diretório /home/labex/project/json_practice.

  2. Adicione o seguinte código a 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. Execute o script no terminal:
cd /home/labex/project/json_practice
python3 nested_arrays.py

Você deve ver uma saída semelhante a:

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

Compreendendo o Acesso a Arrays Aninhados

Trabalhar com arrays aninhados em JSON envolve uma combinação de:

  1. Acesso indexado: Use colchetes com um índice para acessar elementos específicos do array (array[0])
  2. Acesso a propriedades aninhadas: Encadeie chaves de dicionário e índices de array (data['departments'][0]['employees'])
  3. Iteração: Use loops para processar múltiplos itens em arrays

O padrão mais comum para trabalhar com arrays aninhados é usar loops for aninhados:

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

Essa abordagem permite que você percorra estruturas aninhadas complexas e extraia os dados específicos que você precisa.

Lidando com Chaves Ausentes e Prevenção de Erros

Ao trabalhar com estruturas JSON complexas, especialmente de fontes externas como APIs, é importante lidar com potenciais erros que surgem quando chaves estão ausentes. Vamos explorar técnicas para acessar com segurança dados JSON aninhados.

  1. No WebIDE, crie um novo arquivo e salve-o como error_handling.py no diretório /home/labex/project/json_practice.

  2. Adicione o seguinte código a 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. Execute o script no terminal:
cd /home/labex/project/json_practice
python3 error_handling.py

Você deve ver uma saída semelhante a:

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

Compreendendo as Técnicas de Tratamento de Erros

O exemplo demonstra duas abordagens principais para acessar com segurança dados JSON aninhados:

  1. Blocos Try-Except

    • Envolva o acesso potencialmente arriscado em blocos try-except
    • Capture KeyError (chave de dicionário ausente) e TypeError (tentativa de acessar uma chave em um não-dicionário)
    • Forneça valores de fallback quando ocorrem erros
  2. Método get() Encadeado

    • Use o método get() do dicionário, que recebe um valor padrão como seu segundo argumento
    • Encadeie múltiplas chamadas get() para estruturas aninhadas
    • Fornece um código mais limpo sem tratamento de exceção

A abordagem do método get() é geralmente preferida por sua legibilidade e concisão ao lidar com estruturas JSON aninhadas. Ele permite que você forneça valores padrão em cada nível de aninhamento.

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

Usar essas técnicas de tratamento de erros tornará seu código mais robusto ao trabalhar com dados JSON de várias fontes.

Exercício Prático: Construindo um Extrator de Dados JSON

Vamos colocar seu conhecimento em prática criando um programa que extrai informações específicas de uma estrutura JSON complexa. Isso pode representar um cenário do mundo real em que você recebe dados JSON de uma API e precisa processá-los.

  1. No WebIDE, crie um novo arquivo e salve-o como json_extractor.py no diretório /home/labex/project/json_practice.

  2. Adicione o seguinte código a 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. Execute o script no terminal:
cd /home/labex/project/json_practice
python3 json_extractor.py
  1. Após executar o script, você pode visualizar o arquivo de resumo gerado:
cat weather_summary.txt

Você verá o resumo do tempo formatado extraído da estrutura JSON complexa.

Compreendendo o Extrator JSON

Este exercício prático demonstra vários conceitos importantes para trabalhar com dados JSON aninhados:

  1. Extração segura de dados

    • Usando o método get() com valores padrão para lidar com chaves ausentes
    • Acessando dados aninhados com chamadas get() encadeadas
    • Tratamento de erros com blocos try-except
  2. Transformação de dados

    • Convertendo dados JSON em um formato legível por humanos
    • Iterando por arrays aninhados para processar vários itens
    • Extraindo apenas as informações relevantes de uma estrutura complexa
  3. Programação defensiva

    • Antecipando e lidando com possíveis problemas
    • Fornecendo valores padrão quando os dados estão ausentes
    • Capturando exceções para evitar falhas no programa

Este padrão de extração, transformação e apresentação de dados JSON é comum em muitas aplicações do mundo real, como:

  • Processamento de respostas de API
  • Geração de relatórios a partir de dados
  • Filtragem e exibição de informações para usuários

Seguindo esses padrões, você pode trabalhar de forma confiável com dados JSON de qualquer complexidade.

Resumo

Neste laboratório, você aprendeu a trabalhar com estruturas JSON aninhadas em Python:

  1. Manipulação básica de JSON - Convertendo entre objetos Python e strings JSON usando json.dumps() e json.loads().

  2. Acessando chaves de dicionário aninhadas - Usando a notação de colchetes encadeados para acessar valores profundamente aninhados em objetos JSON.

  3. Trabalhando com arrays aninhados - Navegando e extraindo dados de arrays dentro de estruturas JSON usando indexação e iteração.

  4. Técnicas de tratamento de erros - Implementando padrões de acesso seguro com blocos try-except e o método get() para lidar com chaves ausentes.

  5. Extração prática de dados - Construindo uma aplicação completa que extrai, transforma e apresenta dados de uma estrutura JSON complexa.

Essas habilidades são essenciais para trabalhar com dados de APIs, arquivos de configuração e outros cenários de troca de dados. Agora você tem a base para lidar com confiança com dados JSON de qualquer complexidade em suas aplicações Python.

Para aprendizado adicional, considere explorar:

  • Trabalhar com APIs reais que retornam dados JSON
  • Usar bibliotecas como pandas para analisar dados JSON
  • Implementar validação JSON com jsonschema
  • Criar e manipular estruturas JSON personalizadas