웹사이트 검색

jq로 JSON 데이터를 변환하는 방법


저자는 Write for DOnations 프로그램을 선택했습니다.

소개

큰 JSON 파일로 작업할 때 필요한 정보를 찾고 조작하기 어려울 수 있습니다. 모든 관련 스니펫을 복사하여 붙여넣어 총계를 수동으로 계산할 수 있지만 이는 시간이 많이 걸리는 프로세스이며 사람이 실수하기 쉽습니다. 또 다른 옵션은 정보를 찾고 조작하기 위한 범용 도구를 사용하는 것입니다. 모든 최신 Linux 시스템에는 grep의 세 가지 기존 텍스트 처리 유틸리티가 설치되어 있습니다. 이러한 명령은 느슨하게 구조화된 데이터로 작업할 때 유용하지만 JSON과 같은 기계 판독 가능 데이터 형식을 위한 다른 옵션이 있습니다.

명령줄 JSON 처리 도구인 jq는 기계가 읽을 수 있는 데이터 형식을 처리하기 위한 좋은 솔루션이며 특히 셸 스크립트에서 유용합니다. jq를 사용하면 데이터를 조작해야 할 때 도움이 될 수 있습니다. 예를 들어 JSON API에 대한 curl 호출을 실행하면 jq가 서버 응답에서 특정 정보를 추출할 수 있습니다. 데이터 엔지니어로서 데이터 수집 프로세스에 jq를 통합할 수도 있습니다. Kubernetes 클러스터를 관리하는 경우 kubectl의 JSON 출력을 jq의 입력 소스로 사용하여 특정 배포에 사용 가능한 복제본 수를 추출할 수 있습니다.

이 기사에서는 jq를 사용하여 해양 동물에 대한 샘플 JSON 파일을 변환합니다. 필터를 사용하여 데이터 변환을 적용하고 변환된 데이터 조각을 새로운 데이터 구조로 병합합니다. 자습서가 끝나면 jq 스크립트를 사용하여 조작한 데이터에 대한 질문에 답할 수 있습니다.

전제 조건

이 자습서를 완료하려면 다음이 필요합니다.

    JSON 구문 분석 및 변환 도구인
  • jq. 모든 주요 Linux 배포판의 리포지토리에서 사용할 수 있습니다. Ubuntu를 사용하는 경우 sudo apt install jq를 실행하여 설치합니다.
  • JSON 소개에서 새로 고칠 수 있는 JSON 구문에 대한 이해

1단계 - 첫 번째 jq 명령 실행

이 단계에서는 샘플 입력 파일을 설정하고 jq 명령을 실행하여 샘플 파일 데이터의 출력을 생성하여 설정을 테스트합니다. jq는 파일이나 파이프에서 입력을 받을 수 있습니다. 전자를 사용하게 됩니다.

샘플 파일을 생성하여 시작합니다. 원하는 편집기를 사용하여 seaCreatures.json이라는 새 파일을 만들고 엽니다(이 자습서에서는 nano를 사용함).

  1. nano seaCreatures.json

다음 내용을 파일에 복사합니다.

[
    { "name": "Sammy", "type": "shark", "clams": 5 },
    { "name": "Bubbles", "type": "orca", "clams": 3 },
    { "name": "Splish", "type": "dolphin", "clams": 2 },
    { "name": "Splash", "type": "dolphin", "clams": 2 }
]

튜토리얼의 나머지 부분에서 이 데이터로 작업하게 됩니다. 자습서가 끝나면 이 데이터에 대한 다음 질문에 답하는 한 줄 jq 명령을 작성하게 됩니다.

  • 목록 형식의 바다 생물의 이름은 무엇입니까?
  • 생물이 총 몇 마리의 조개를 소유하고 있습니까?
  • 돌고래 소유의 조개는 몇 개입니까?

파일을 저장하고 닫습니다.

입력 파일 외에도 JSON 입력을 변경하지 않고 출력으로 전달하는 ID 연산자가 필요합니다.

ID 연산자를 사용하여 설정이 작동하는지 테스트할 수 있습니다. 구문 분석 오류가 표시되면 seaCreatures.json에 유효한 JSON이 포함되어 있는지 확인하십시오.

다음 명령을 사용하여 ID 연산자를 JSON 파일에 적용합니다.

  1. jq '.' seaCreatures.json

파일과 함께 jq를 사용할 때 항상 입력 파일 다음에 필터를 전달합니다. 필터에는 셸에 특별한 의미가 있는 공백 및 기타 문자가 포함될 수 있으므로 필터를 작은따옴표로 묶는 것이 좋습니다. 이렇게 하면 필터가 명령 매개변수임을 쉘에 알립니다. jq를 실행해도 원본 파일이 수정되지 않으므로 안심하십시오.

다음과 같은 결과가 표시됩니다.

Output
[ { "name": "Sammy", "type": "shark", "clams": 5 }, { "name": "Bubbles", "type": "orca", "clams": 3 }, { "name": "Splish", "type": "dolphin", "clams": 2 }, { "name": "Splash", "type": "dolphin", "clams": 2 } ]

기본적으로 jq는 출력을 예쁘게 인쇄합니다. 자동으로 들여쓰기를 적용하고 모든 값 뒤에 새 줄을 추가하며 가능한 경우 출력에 색상을 지정합니다. 색상을 지정하면 가독성이 향상되어 많은 개발자가 다른 도구에서 생성된 JSON 데이터를 검토할 때 도움이 될 수 있습니다. 예를 들어 JSON API에 curl 요청을 보낼 때 JSON 응답을 jq .로 파이프하여 예쁘게 인쇄할 수 있습니다.

이제 jq가 실행되고 있습니다. 입력 파일이 설정되면 creatures, totalClamstotalDolphinClams. 다음 단계에서는 creatures 값에서 정보를 찾을 수 있습니다.

2단계 - 생물 값 검색

이 단계에서는 이름을 찾기 위해 creatures 값을 사용하여 모든 바다 생물 목록을 생성합니다. 이 단계가 끝나면 다음과 같은 이름 목록이 생성됩니다.

Output
[ "Sammy", "Bubbles", "Splish", "Splash" ],

이 목록을 생성하려면 생물의 이름을 추출한 다음 배열로 병합해야 합니다.

모든 생물의 이름을 얻고 나머지는 모두 버리려면 필터를 수정해야 합니다. 배열에 대해 작업하고 있으므로 jq에 배열 자체가 아니라 해당 배열의 값에 대해 작업하고 싶다고 알려야 합니다. .[]로 작성된 배열 값 반복자는 이러한 용도로 사용됩니다.

수정된 필터로 jq를 실행합니다.

  1. jq '.[]' seaCreatures.json

이제 모든 배열 값이 별도로 출력됩니다.

Output
{ "name": "Sammy", "type": "shark", "clams": 5 } { "name": "Bubbles", "type": "orca", "clams": 3 } { "name": "Splish", "type": "dolphin", "clams": 2 } { "name": "Splash", "type": "dolphin", "clams": 2 }

모든 배열 항목을 전체로 출력하는 대신 name 속성의 값을 출력하고 나머지는 버릴 수 있습니다. 파이프 연산자 |를 사용하면 각 출력에 필터를 적용할 수 있습니다. find를 사용한 경우 | xargs를 사용하여 모든 검색 결과에 명령을 적용하면 이 패턴이 친숙하게 느껴질 것입니다.

JSON 개체의 name 속성은 .name을 작성하여 액세스할 수 있습니다. 파이프를 필터와 결합하고 seaCreatures.json에서 다음 명령을 실행합니다.

  1. jq '.[] | .name' seaCreatures.json

출력에서 다른 속성이 사라진 것을 알 수 있습니다.

Output
"Sammy" "Bubbles" "Splish" "Splash"

기본적으로 jq는 유효한 JSON을 출력하므로 문자열은 큰따옴표(\\) 안에 표시됩니다. 큰따옴표가 없는 문자열이 필요한 경우 -r 플래그를 추가하여 원시 출력을 활성화합니다.

  1. jq -r '.[] | .name' seaCreatures.json

따옴표가 사라졌습니다.

Output
Sammy Bubbles Splish Splash

이제 JSON 입력에서 특정 정보를 추출하는 방법을 알게 되었습니다. 이 기술을 사용하여 다음 단계에서 다른 특정 정보를 찾은 다음 마지막 단계에서 creatures 값을 생성합니다.

3단계 — map 및 add로 totalClams 값 계산

이 단계에서는 생물이 소유한 조개 수에 대한 총 정보를 찾을 수 있습니다. 몇 가지 데이터를 집계하여 답을 계산할 수 있습니다. jq에 익숙해지면 수동 계산보다 빠르고 인적 오류가 덜 발생합니다. 이 단계의 끝에서 예상되는 값은 12입니다.

2단계에서는 항목 목록에서 특정 정보를 추출했습니다. 이 기술을 재사용하여 clams 속성 값을 추출할 수 있습니다. 이 새 속성에 대한 필터를 조정하고 다음 명령을 실행합니다.

  1. jq '.[] | .clams' seaCreatures.json

clams 속성의 개별 값이 출력됩니다.

Output
5 3 2 2

개별 값의 합계를 찾으려면 add 필터가 필요합니다. add 필터는 배열에서 작동합니다. 그러나 현재 배열 값을 출력하고 있으므로 먼저 배열로 래핑해야 합니다.

다음과 같이 기존 필터를 []로 묶습니다.

  1. jq '[.[] | .clams]' seaCreatures.json

값이 목록에 나타납니다.

Output
[ 5, 3, 2, 2 ]

add 필터를 적용하기 전에 map 기능을 사용하여 명령의 가독성을 향상시킬 수 있으며 유지 관리도 더 쉬워집니다. 하나의 map 호출로 배열을 반복하고 각 항목에 필터를 적용한 다음 결과를 배열로 래핑할 수 있습니다. 항목 배열이 주어지면 map은 인수를 각 항목에 대한 필터로 적용합니다. 예를 들어 map(.name) 필터를 [{\name\: \Sammy\}에 적용하면 { name: Bubbles}], 결과 JSON 개체는 [Sammy, Bubbles]입니다.

대신 map 함수를 사용하도록 배열을 생성하도록 필터를 다시 작성한 다음 실행합니다.

  1. jq 'map(.clams)' seaCreatures.json

이전과 동일한 출력을 받게 됩니다.

Output
[ 5, 3, 2, 2 ]

이제 배열이 있으므로 add 필터로 파이프할 수 있습니다.

  1. jq 'map(.clams) | add' seaCreatures.json

배열의 합계를 받게 됩니다.

Output
12

이 필터를 사용하여 나중에 totalClams 값을 생성하는 데 사용할 총 조개 수를 계산했습니다. 세 가지 질문 중 두 가지에 대한 필터를 작성했습니다. 생성할 필터가 하나 더 있으며 그 후에 최종 출력을 생성할 수 있습니다.

4단계 - 추가 필터로 totalDolphinClams 값 계산

이제 생물이 얼마나 많은 조개를 소유하고 있는지 알았으므로 돌고래가 얼마나 많은 조개를 가지고 있는지 식별할 수 있습니다. 특정 조건을 만족하는 배열 요소의 값만 추가하여 답을 생성할 수 있습니다. 이 단계의 끝에서 예상되는 값은 4이며 돌고래가 가지고 있는 총 조개 수입니다. 마지막 단계에서 결과 값은 totalDolphinClams 속성에서 사용됩니다.

3단계에서 했던 것처럼 모든 조개 값을 추가하는 대신 \돌고래\ 유형의 생물이 보유한 조개만 계산합니다. select(condition)와 같이 select 함수를 사용하여 특정 조건을 선택합니다. 조건이 true로 평가되는 모든 입력이 전달됩니다. 다른 모든 입력은 무시됩니다. 예를 들어 JSON 입력이 \dolphin\이고 필터가 select(. == \dolphin\)인 경우 출력은 \dolphin\입니다. . 입력 \Sammy\의 경우 동일한 필터가 아무 것도 출력하지 않습니다.

배열의 모든 값에 select를 적용하려면 map과 쌍을 이룰 수 있습니다. 이때 조건을 만족하지 않는 배열 값은 폐기됩니다.

귀하의 경우 type 값이 \dolphin\인 배열 값만 유지하려고 합니다. 결과 필터는 다음과 같습니다.

  1. jq 'map(select(.type == "dolphin"))' seaCreatures.json

필터는 상어 Sammy 및 범고래 Bubbles와 일치하지 않지만 두 마리의 돌고래와 일치합니다.

Output
[ { "name": "Splish", "type": "dolphin", "clams": 2 }, { "name": "Splash", "type": "dolphin", "clams": 2 } ]

이 출력에는 생물당 조개 수와 관련 없는 일부 정보가 포함됩니다. clams 값만 유지하려면 map의 매개변수 끝에 필드 이름을 추가할 수 있습니다.

  1. jq 'map(select(.type == "dolphin").clams)' seaCreatures.json

map 함수는 배열을 입력으로 받고 map의 필터(인수로 전달됨)를 각 배열 요소에 적용합니다. 결과적으로 select는 생물당 한 번씩 총 4번 호출됩니다. select 함수는 두 돌고래에 대한 출력을 생성하고(조건과 일치하므로) 나머지는 생략합니다.

출력은 일치하는 두 생물의 clams 값만 포함하는 배열이 됩니다.

Output
[ 2, 2 ]

배열 값을 add로 파이프합니다.

  1. jq 'map(select(.type == "dolphin").clams) | add' seaCreatures.json

출력은 \돌고래\ 유형의 생물에서 조개 값의 합계를 반환합니다.

Output
4

mapselect를 성공적으로 결합하여 배열에 액세스하고, 조건과 일치하는 배열 항목을 선택하고, 변환하고, 해당 변환의 결과를 합산했습니다. 이 전략을 사용하여 최종 출력에서 totalDolphinClams를 계산할 수 있습니다. 이 작업은 다음 단계에서 수행합니다.

5단계 - 데이터를 새로운 데이터 구조로 변환

이전 단계에서 필터를 작성하여 샘플 데이터를 추출하고 조작했습니다. 이제 이러한 필터를 결합하여 데이터에 대한 질문에 답하는 출력을 생성할 수 있습니다.

  • 목록 형식의 바다 생물의 이름은 무엇입니까?
  • 생물이 총 몇 마리의 조개를 소유하고 있습니까?
  • 돌고래 소유의 조개는 몇 개입니까?

목록 형식으로 바다 생물의 이름을 찾기 위해 map 함수를 사용했습니다: map(.name). 생물이 총 몇 개의 조개를 소유하고 있는지 알아보기 위해 모든 clams 값을 add 필터에 연결했습니다. map(.clams) | 추가. 돌고래가 소유한 조개의 수를 알아보기 위해 .type == \dolphin\ 조건과 함께 select 함수를 사용했습니다. map(select(. type == 돌고래).clams) | 추가.

이러한 필터를 모든 작업을 수행하는 하나의 jq 명령으로 결합합니다. 원하는 정보를 표시하는 새 데이터 구조를 만들기 위해 세 가지 필터를 병합하는 새 JSON 개체를 만듭니다.

참고로 시작 JSON 파일은 다음과 일치합니다.

[
    { "name": "Sammy", "type": "shark", "clams": 5 },
    { "name": "Bubbles", "type": "orca", "clams": 3 },
    { "name": "Splish", "type": "dolphin", "clams": 2 },
    { "name": "Splash", "type": "dolphin", "clams": 2 }
]

변환된 JSON 출력은 다음을 생성합니다.

Final Output
{ "creatures": [ "Sammy", "Bubbles", "Splish", "Splash" ], "totalClams": 12, "totalDolphinClams": 4 }

다음은 입력 값이 비어 있는 전체 jq 명령의 구문 데모입니다.

  1. jq '{ creatures: [], totalClams: 0, totalDolphinClams: 0 }' seaCreatures.json

이 필터를 사용하여 세 가지 속성을 포함하는 JSON 객체를 생성합니다.

Output
{ "creatures": [], "totalClams": 0, "totalDolphinClams": 0 }

최종 출력처럼 보이기 시작했지만 seaCreatures.json 파일에서 가져오지 않았기 때문에 입력 값이 올바르지 않습니다.

하드 코딩된 속성 값을 각 이전 단계에서 만든 필터로 바꿉니다.

  1. jq '{ creatures: map(.name), totalClams: map(.clams) | add, totalDolphinClams: map(select(.type == "dolphin").clams) | add }' seaCreatures.json

위의 필터는 jq에게 다음을 포함하는 JSON 개체를 생성하도록 지시합니다.

  • 모든 생물의 이름 값 목록을 포함하는 생물 속성
  • 모든 생물의 clams 값의 합계를 포함하는 totalClams 속성입니다.
  • type\dolphin\인 모든 생물의 clams 값의 합계를 포함하는 totalDolphinClams 속성입니다.

명령을 실행하면 이 필터의 출력은 다음과 같아야 합니다.

Output
{ "creatures": [ "Sammy", "Bubbles", "Splish", "Splash" ], "totalClams": 12, "totalDolphinClams": 4 }

이제 세 가지 질문 모두에 대한 관련 데이터를 제공하는 단일 JSON 개체가 있습니다. 데이터 세트가 변경되면 작성한 jq 필터를 사용하여 언제든지 변환을 다시 적용할 수 있습니다.

결론

JSON 입력으로 작업할 때 jqsed와 같은 텍스트 조작 도구로는 어려울 수 있는 다양한 데이터 변환을 수행하는 데 도움이 될 수 있습니다. 이 자습서에서는 select 함수를 사용하여 데이터를 필터링하고 map을 사용하여 배열 요소를 변환하고 add 필터를 사용하여 숫자 배열을 합산하고 학습했습니다. 변환을 새 데이터 구조로 병합하는 방법.

jq 고급 기능에 대해 알아보려면 모든 형식에서 작동하는 텍스트 처리 기술에 대한 정보를 위해 grep을 자세히 살펴보십시오.