JSON 로더

  • JSON (JavaScript Object Notation)은 속성-값 쌍이나 배열과 같은 값들을 포함하는 데이터 객체를 저장하고 전송하기 위해 인간이 읽을 수 있는 텍스트를 사용하는 개방형 표준 파일 형식 및 데이터 교환 형식입니다.

JSON Lines은 각 행이 유효한 JSON 값인 파일 형식입니다.

JSONLoader는 지정된 jq 패턴을 사용하여 JSON 파일을 구문 분석하고 jq Python 패키지를 활용합니다. jq 구문에 대한 자세한 설명은 관련 Python 문서를 참조하십시오.

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

file_path = './example_data/facebook_chat.json'
data = json.loads(Path(file_path).read_text())
pprint(data)
{'image': {'creation_timestamp': 1675549016, 'uri': 'image_of_the_chat.jpg'},
     'is_still_participant': True,
     'joinable_mode': {'link': '', 'mode': 1},
     'magic_words': [],
     'messages': [{'content': '안녕!',
                   'sender_name': '사용자 2',
                   'timestamp_ms': 1675597571851},
                  {'content': '아니 괜찮아! 안녕',
                   'sender_name': '사용자 1',
                   'timestamp_ms': 1675597435669},
                  {'content': '아니 미안했어, 그 파란 건 팔지 않아',
                   'sender_name': '사용자 2',
                   'timestamp_ms': 1675596277579},
                  {'content': '너도 블루를 팔고 있는 줄 알았어!',
                   'sender_name': '사용자 1',
                   'timestamp_ms': 1675595140251},
                  {'content': '내가 이 가방에 관심이 없어. 난 블루가 관심 있어!',
                   'sender_name': '사용자 1',
                   'timestamp_ms': 1675595109305},
                  {'content': '여기 $129',
                   'sender_name': '사용자 2',
                   'timestamp_ms': 1675595068468},
                  {'photos': [{'creation_timestamp': 1675595059,
                               'uri': 'url_of_some_picture.jpg'}],
                   'sender_name': '사용자 2',
                   'timestamp_ms': 1675595060730},
                  {'content': '온라인에서는 최소 $100이야',
                   'sender_name': '사용자 2',
                   'timestamp_ms': 1675595045152},
                  {'content': '얼마에 팔래?',
                   'sender_name': '사용자 1',
                   'timestamp_ms': 1675594799696},
                  {'content': '좋은 아침! $50은 너무 적어.',
                   'sender_name': '사용자 2',
                   'timestamp_ms': 1675577876645},
                  {'content': '안녕! 당신의 가방에 관심이 있어. $50으로 제안하는 중이야. 관심이 있으면 알려주세요. 감사합니다!',
                   'sender_name': '사용자 1',
                   'timestamp_ms': 1675549022673}],
     'participants': [{'name': '사용자 1'}, {'name': '사용자 2'}],
     'thread_path': 'inbox/사용자 1 및 사용자 2 대화',
     'title': '사용자 1 및 사용자 2 대화'}

JSONLoader 사용하기

만약 JSON 데이터에서 messages 키 아래의 content 필드에서 값들을 추출하고 싶다면, JSONLoader를 활용하여 이 작업을 손쉽게 수행할 수 있습니다.

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

data = loader.load()
pprint(data)
[Document(page_content='안녕!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1}),
     Document(page_content='아니 괜찮아요! 안녕히가세요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2}),
     Document(page_content='아니 죄송해요, 제 실수였어요. 파란색은 판매 대상이 아니에요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 3}),
     Document(page_content='파란색 제품을 판다고 생각했어요!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 4}),
     Document(page_content='이 가방에는 관심 없어요. 파란색 제품에 관심이 있어요!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 5}),
     Document(page_content='$129여기 있어요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 6}),
     Document(page_content='', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 7}),
     Document(page_content='온라인에서 최소한 $100이에요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 8}),
     Document(page_content='얼마에요?', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 9}),
     Document(page_content='좋은 아침! $50은 너무 낮아요.', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 10}),
     Document(page_content='안녕하세요! 당신의 가방에 흥미가 있어요. $50을 제안하고 있어요. 흥미가 있으시면 알려주세요. 감사합니다!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11})]

JSON Lines 파일

JSON Lines 파일에서 문서를 로드하려면 json_lines=True를 전달하고 단일 JSON 객체에서 page_content를 추출하기 위해 jq_schema를 지정해야합니다.

file_path = './example_data/facebook_chat_messages.jsonl'
pprint(Path(file_path).read_text())
('{"sender_name": "User 2", "timestamp_ms": 1675597571851, "content": "잘 가!"}\n'
     '{"sender_name": "User 1", "timestamp_ms": 1675597435669, "content": "안녕 안녕! 잘 가"}\n'
     '{"sender_name": "User 2", "timestamp_ms": 1675596277579, "content": "아니 미안했어, 내 실수야, 파란 건 팔지 않아"}\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='잘 가!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 1}),
     Document(page_content='안녕 안녕! 잘 가', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 2}),
     Document(page_content='아니 미안했어, 내 실수야, 파란 건 팔지 않아', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 3})]

다른 옵션은 jq_schema='.'로 설정하고 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='User 2', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 1}),
     Document(page_content='User 1', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 2}),
     Document(page_content='User 2', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat_messages.jsonl', 'seq_num': 3})]

메타데이터 추출

보통 우리는 콘텐츠에서 JSON 파일의 메타데이터를 문서에 포함시키고 싶어합니다.

다음은 JSONLoader를 사용하여 메타데이터를 추출하는 방법을 보여줍니다.

이전 예제에서는 메타데이터를 수집하지 않았지만, 스키마에서 page_content 값을 직접 추출할 수 있는 위치를 지정했습니다.

.messages[].content

현재 예제에서는 로더에게 messages 필드의 레코드를 반복하도록 지시해야 합니다. 그런 다음 jq_schema는 다음과 같아야 합니다.

.messages[]

이렇게 하면 레코드(딕셔너리)를 "metadata_func"에 전달하여 구현해야 하는 "metadata_func"가 메타데이터에 포함될 레코드 정보를 식별하도록 할 수 있습니다. "metadata_func"은 최종 "Document" 객체에 저장될 메타데이터를 식별하는 역할을 합니다.

추가적으로, 이제 로더에서 "content_key" 매개변수를 명시적으로 지정하여 레코드에서 "page_content" 값을 추출해야 합니다.

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

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

    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)
[문서(page_content='잘 가!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': '사용자 2', 'timestamp_ms': 1675597571851}),
     문서(page_content='아니 괜찮아! 잘 가', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2, 'sender_name': '사용자 1', 'timestamp_ms': 1675597435669}),
     문서(page_content='안녕, 죄송해요. 저의 실수였어요. 파란색은 판매 대상이 아니에요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 3, 'sender_name': '사용자 2', 'timestamp_ms': 1675596277579}),
     문서(page_content='파란 걸 판다고 생각했어요!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 4, 'sender_name': '사용자 1', 'timestamp_ms': 1675595140251}),
     문서(page_content='이 가방에는 관심 없어요. 파란 건에 관심이 있어요!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 5, 'sender_name': '사용자 1', 'timestamp_ms': 1675595109305}),
     문서(page_content='$129 여기 있어요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 6, 'sender_name': '사용자 2', 'timestamp_ms': 1675595068468}),
     문서(page_content='', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 7, 'sender_name': '사용자 2', 'timestamp_ms': 1675595060730}),
     문서(page_content='온라인으로는 최소 $100에요', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 8, 'sender_name': '사용자 2', 'timestamp_ms': 1675595045152}),
     문서(page_content='얼마에 팔려고 해요?', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 9, 'sender_name': '사용자 1', 'timestamp_ms': 1675594799696}),
     문서(page_content='좋은 아침! $50은 너무 낮아요.', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 10, 'sender_name': '사용자 2', 'timestamp_ms': 1675577876645}),
     문서(page_content='안녕! 당신의 가방에 관심이 있어요. $50 제안하고 있어요. 관심 있으면 알려주세요. 감사합니다!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11, 'sender_name': '사용자 1', 'timestamp_ms': 1675549022673})]

여기서 우리는 이미 추출한 내용과 관련된 메타데이터가 이미 파일에 포함되어 있는 것을 볼 수 있습니다.

metadata_func 함수

위에서 볼 수 있듯이, metadata_func 함수는 JSONLoader에 의해 생성된 기본 메타데이터를 받습니다. 이를 통해 사용자는 메타데이터 형식을 완전히 제어할 수 있게 됩니다.

예를 들어, 기본 메타데이터에는 sourceseq_num 키가 포함되어 있습니다. 그러나 JSON 데이터에도 이러한 키가 포함될 수 있습니다. 사용자들은 metadata_func 함수를 사용하여 기본 키의 이름을 변경하고 JSON 데이터의 키를 사용할 수 있습니다.

다음 예제는 source를 수정하여 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='Bye!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': 'User 2', 'timestamp_ms': 1675597571851}),
     Document(page_content='Oh no worries! Bye', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 2, 'sender_name': 'User 1', 'timestamp_ms': 1675597435669}),
     ...
     ...
     ... (간결함을 위해 나머지 내용은 변경되지 않음) ...
     ...
     ...
]

jq 패턴을 사용한 일반적인 JSON 구조

다음 목록은 사용자가 구조에 따라 JSON 데이터에서 내용을 추출하는 데 참조할 수 있는 가능한 jq_schema를 제공합니다.

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

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

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