Carregador de JSON

  • O JSON (JavaScript Object Notation) é um formato de arquivo padrão aberto e formato de troca de dados que utiliza texto legível para armazenar e transmitir objetos de dados compostos por pares de atributos e valores e arrays (ou outros valores serializáveis).

JSON Lines é um formato de arquivo onde cada linha é um valor JSON válido.

O JSONLoader analisa arquivos JSON usando um padrão jq especificado e utiliza o pacote jq Python. Para documentação detalhada sobre a sintaxe jq, consulte a documentação relevante do Python.

from langchain_community.document_loaders import JSONLoader
import json
from pathlib import Path
from pprint import pprint

caminho_arquivo = './exemplo_dados/conversa_facebook.json'
dados = json.loads(Path(caminho_arquivo).read_text())
pprint(dados)
{'image': {'creation_timestamp': 1675549016, 'uri': 'imagem_do_chat.jpg'},
     'is_still_participant': True,
     'joinable_mode': {'link': '', 'mode': 1},
     'magic_words': [],
     'messages': [{'content': 'Tchau!',
                   'sender_name': 'Usuário 2',
                   'timestamp_ms': 1675597571851},
                  {'content': 'Ah, sem problemas! Tchau',
                   'sender_name': 'Usuário 1',
                   'timestamp_ms': 1675597435669},
                  {'content': 'Não, desculpe, foi meu erro, o azul não está à '
                              'venda',
                   'sender_name': 'Usuário 2',
                   'timestamp_ms': 1675596277579},
                  {'content': 'Eu pensei que você estava vendendo o azul!',
                   'sender_name': 'Usuário 1',
                   'timestamp_ms': 1675595140251},
                  {'content': 'Não estou interessado nesta bolsa. Estou interessado '
                              'na azul!',
                   'sender_name': 'Usuário 1',
                   'timestamp_ms': 1675595109305},
                  {'content': 'Aqui está $129',
                   'sender_name': 'Usuário 2',
                   'timestamp_ms': 1675595068468},
                  {'photos': [{'creation_timestamp': 1675595059,
                               'uri': 'url_de_alguma_imagem.jpg'}],
                   'sender_name': 'Usuário 2',
                   'timestamp_ms': 1675595060730},
                  {'content': 'Online está pelo menos $100',
                   'sender_name': 'Usuário 2',
                   'timestamp_ms': 1675595045152},
                  {'content': 'Quanto você quer?',
                   'sender_name': 'Usuário 1',
                   'timestamp_ms': 1675594799696},
                  {'content': 'Bom dia! $50 é muito pouco.',
                   'sender_name': 'Usuário 2',
                   'timestamp_ms': 1675577876645},
                  {'content': 'Oi! Estou interessado na sua bolsa. Estou oferecendo '
                              '$50. Me avise se você estiver interessado. Obrigado!',
                   'sender_name': 'Usuário 1',
                   'timestamp_ms': 1675549022673}],
     'participants': [{'name': 'Usuário 1'}, {'name': 'Usuário 2'}],
     'thread_path': 'inbox/Conversa entre Usuário 1 e Usuário 2',
     'title': 'Conversa entre Usuário 1 e Usuário 2'}

Usando JSONLoader

Suponha que queremos extrair os valores do campo conteúdo sob a chave mensagens nos dados JSON. Utilizando o JSONLoader, podemos facilmente realizar essa tarefa.

loader = JSONLoader(
    file_path='./exemplo_dados/conversa_facebook.json',
    jq_schema='.mensagens[].conteúdo')

dados = loader.load()
pprint(dados)
[Documento(conteúdo_da_página='Tchau!', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 1}),
     Documento(conteúdo_da_página='Ah, sem problemas! Tchau', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 2}),
     Documento(conteúdo_da_página='Não, desculpe, foi meu erro, o azul não está à venda', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 3}),
     Documento(conteúdo_da_página='Pensei que você estava vendendo o azul!', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 4}),
     Documento(conteúdo_da_página='Não tenho interesse nessa bolsa. Estou interessado no azul!', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 5}),
     Documento(conteúdo_da_página='Aqui está $129', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 6}),
     Documento(conteúdo_da_página='', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 7}),
     Documento(conteúdo_da_página='Online está pelo menos $100', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 8}),
     Documento(conteúdo_da_página='Quanto você quer?', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 9}),
     Documento(conteúdo_da_página='Bom dia! $50 é muito pouco.', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 10}),
     Documento(conteúdo_da_página='Oi! Estou interessado na sua bolsa. Estou oferecendo $50. Me avise se você estiver interessado. Obrigado!', metadados={'fonte': '/Usuários/avsolatorio/WBG/langchain/docs/módulos/índices/carregadores_de_documento/exemplos/exemplo_dados/conversa_facebook.json', 'num_seq': 11})]

Ficheiro de linhas JSON

Se deseja carregar documentos de um ficheiro de linhas JSON, precisa de passar json_lines=True e especificar o jq_schema para extrair o page_content de um único objeto JSON.

file_path = './example_data/facebook_chat_messages.jsonl'
pprint(Path(file_path).read_text())
('{"sender_name": "Utilizador 2", "timestamp_ms": 1675597571851, "content": "Adeus!"}\n'
     '{"sender_name": "Utilizador 1", "timestamp_ms": 1675597435669, "content": "Oh, não se preocupe! Adeus"}\n'
     '{"sender_name": "Utilizador 2", "timestamp_ms": 1675596277579, "content": "Não, desculpe, foi meu erro, o azul não está à venda"}\n')
loader = JSONLoader(
    file_path='./example_data/facebook_chat_messages.jsonl',
    jq_schema='.content',
    json_lines=True)

data = loader.load()
pprint(data)
[Document(page_content='Adeus!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 1}),
     Document(page_content='Oh, não se preocupe! Adeus', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 2}),
     Document(page_content='Não, desculpe, foi meu erro, o azul não está à venda', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 3})]

Outra opção é definir jq_schema='.' e fornecer a content_key:

loader = JSONLoader(
    file_path='./example_data/facebook_chat_messages.jsonl',
    jq_schema='.',
    content_key='sender_name',
    json_lines=True)

data = loader.load()
pprint(data)
[Document(page_content='Utilizador 2', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 1}),
     Document(page_content='Utilizador 1', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 2}),
     Document(page_content='Utilizador 2', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 3})]

Extração de Metadados

Normalmente, gostaríamos de incluir os metadados de um arquivo JSON no documento que criamos a partir do conteúdo.

A seguir, demonstra-se como usar o JSONLoader para extrair metadados.

No exemplo anterior, não coletamos metadados, mas conseguimos especificar no esquema a localização da qual o valor page_content pode ser extraído diretamente.

.messages[].content

No exemplo atual, precisamos dizer ao carregador para iterar sobre os registros no campo messages. Em seguida, o jq_schema deve ser:

.messages[]

Isso nos permite passar os registros (dicionários) para a "metadata_func" que deve ser implementada. A "metadata_func" é responsável por identificar quais informações de registro devem ser incluídas nos metadados a serem armazenados no objeto "Document" final.

Além disso, agora temos que especificar explicitamente o parâmetro content_key no carregador para extrair o valor "page_content" de qual chave no registro.

def metadata_func(registro: dict, metadados: dict) -> dict:

    metadados["nome_remetente"] = registro.get("nome_remetente")
    metadados["timestamp_ms"] = registro.get("timestamp_ms")

    return metadados

carregador = JSONLoader(
    file_path='./exemplo_dados/conversa_facebook.json',
    jq_schema='.messages[]',
    content_key="content",
    metadata_func=metadata_func
)

dados = carregador.load()
pprint(dados)
[Documento(conteúdo_da_página='Tchau!', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'nome_do_remetente': 'Usuário 2', 'timestamp_ms': 1675597571851}),
     Documento(conteúdo_da_página='Ah, não se preocupe! Tchau', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2, 'nome_do_remetente': 'Usuário 1', 'timestamp_ms': 1675597435669}),
     Documento(conteúdo_da_página='Não, desculpe, foi meu engano. O azul não está à venda', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 3, 'nome_do_remetente': 'Usuário 2', 'timestamp_ms': 1675596277579}),
     Documento(conteúdo_da_página='Pensei que você estava vendendo o azul!', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 4, 'nome_do_remetente': 'Usuário 1', 'timestamp_ms': 1675595140251}),
     Documento(conteúdo_da_página='Não estou interessado nesta bolsa. Estou interessado na azul!', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 5, 'nome_do_remetente': 'Usuário 1', 'timestamp_ms': 1675595109305}),
     Documento(conteúdo_da_página='Aqui está $129', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 6, 'nome_do_remetente': 'Usuário 2', 'timestamp_ms': 1675595068468}),
     Documento(conteúdo_da_página='', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 7, 'nome_do_remetente': 'Usuário 2', 'timestamp_ms': 1675595060730}),
     Documento(conteúdo_da_página='Online está pelo menos $100', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 8, 'nome_do_remetente': 'Usuário 2', 'timestamp_ms': 1675595045152}),
     Documento(conteúdo_da_página='Quanto você quer?', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 9, 'nome_do_remetente': 'Usuário 1', 'timestamp_ms': 1675594799696}),
     Documento(conteúdo_da_página='Bom dia! $50 é muito pouco.', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 10, 'nome_do_remetente': 'Usuário 2', 'timestamp_ms': 1675577876645}),
     Documento(conteúdo_da_página='Oi! Estou interessado na sua bolsa. Estou oferecendo $50. Me avise se estiver interessado. Obrigado!', metadados={'origem': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11, 'nome_do_remetente': 'Usuário 1', 'timestamp_ms': 1675549022673})]

Aqui podemos ver que esses arquivos já contêm metadados relacionados ao conteúdo que extraímos.

Função metadata_func

Como mostrado acima, a função metadata_func recebe os metadados padrão gerados pelo JSONLoader. Isso permite que os usuários tenham controle total sobre o formato dos metadados.

Por exemplo, os metadados padrão contêm as chaves source e seq_num. No entanto, os dados JSON também podem conter essas chaves. Os usuários podem usar a função metadata_func para renomear as chaves padrão e usar as chaves dos dados JSON.

O exemplo a seguir demonstra como modificar o source para incluir apenas informações de origem do arquivo relacionadas ao diretório langchain.

def metadata_func(record: dict, metadata: dict) -> dict:

    metadata["sender_name"] = record.get("sender_name")
    metadata["timestamp_ms"] = record.get("timestamp_ms")

    if "source" in metadata:
        source = metadata["source"].split("/")
        source = source[source.index("langchain"):]
        metadata["source"] = "/".join(source)

    return metadata

loader = JSONLoader(
    file_path='./example_data/facebook_chat.json',
    jq_schema='.messages[]',
    content_key="content",
    metadata_func=metadata_func
)

data = loader.load()
pprint(data)
[Document(page_content='Tchau!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': 'Usuário 2', 'timestamp_ms': 1675597571851}),
     Document(page_content='Ah, sem problemas! Tchau', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2, 'sender_name': 'Usuário 1', 'timestamp_ms': 1675597435669}),
     ...
     ...
     ... (restante do conteúdo inalterado por brevidade) ...
     ...
     ...
]

Estruturas comuns de JSON para uso de padrões jq

A lista a seguir fornece possíveis jq_schema que os usuários podem usar como referência para extrair conteúdo de dados JSON com base na estrutura.

JSON        -> [{"text": ...}, {"text": ...}, {"text": ...}]
jq_schema   -> ".[].text"

JSON        -> {"key": [{"text": ...}, {"text": ...}, {"text": ...}]}
jq_schema   -> ".key[].text"

JSON        -> ["...", "...", "..."]
jq_schema   -> ".[]"