Python json file 파싱하기
직접 json 데이터를 파싱해서 Mask R-CNN 모델에 사용할 라벨링 파일로 변환해야 하는 일이 생겼다ㅠ
json 데이터 파싱을 간단한 것만 해봤던지라 복잡한 구조의 json 파일을 보니 머리가 뺑뺑 돌고ㅠ 간단한 것 파싱해본 것도 필요할 때만 잠깐 검색해서 하고 그랬어서 이번 기회에! json 파일 파싱하는 것과 복잡한 구조의 json 파일을 직접 파싱해보는 것까지 진행해본 것을 기록 해두기로 했다.
json 파일 파싱하기
해당 부분은 아래 링크 글 참고하여 작성했습니다.
https://popcorn16.tistory.com/12
파이썬으로 JSON 파싱하기
openAPI로 데이터를 받을 때 JSON에 대해서 처음 알게 되었습니다. 대부분의 API가 JSON 형식으로 데이터를 주기 때문에 꼭 알아야 하지만, 하지만 제대로 공부하지 않고 필요할 때만 급급하게 사용해
popcorn16.tistory.com
1. 간단한 구조의 json file 파싱하기 - id 추출
우선, 간단한 구조의 json file부터 파싱해 보겠습니다.
# user_info.json
{
"id": 1,
"info": {"name": "gellygelly", "email": "112222@gmail.com"},
"hobby": ['yoga', 'pilates', 'game']
}
파싱하고자 하는 json 파일이 위와 같은 구조일 때, 먼저 id를 추출해보겠습니다.
json 파일을 보면 아래와 같이 Key, Value 쌍으로 이루어져 있는데요.
파이썬의 딕셔너리 자료형과 똑같이 생긴 모습인데, 그래서인지 딕셔너리 자료형 다루듯이 데이터 추출이 가능합니다.
{"id": 1}
- jsonObject(객체명).get('key값')
- jsonObejct(객체명)['key값']
import json
user_info = '{"id": 1}'
jsonObject = json.loads(user_info)
print(jsonObject.get("id"))
print(jsonObject["id"])
## 실행 결과
1
1
2. 중첩된 구조의 json object 추출하기 - info 추출
이번엔 중첩된 구조의 json object를 추출해 보겠습니다.
'info' key를 보면, 안쪽에 'name', 'email'이라는 key가 또 존재하는데요.
아래와 같이 하면 중첩된 구조로 된 값도 추출할 수 있습니다.
import json
# user_info.json
user_info = '{
"id": 1,
"info": {"name": "gellygelly", "email": "112222@gmail.com"},
"hobby": ["yoga", "pilates", "game"]
}'
jsonObject = json.loads(user_info)
print(jsonObject.get("info").get("name")) # get 두 번 써서 name 값 가져오기
print(jsonObject["info"]["email"])
## 실행 결과
gellygelly
112222@gmail.com
3. array 형태의 데이터를 가지는 경우 데이터 추출하기 - hobby
이번엔 array 형태의 데이터를 가지는 'hobby' key의 value를 추출해 보겠습니다.
import json
# user_info.json
user_info = '{
"id": 1,
"info": {"name": "gellygelly", "email": "112222@gmail.com"},
"hobby": ["yoga", "pilates", "game"]
}'
jsonObject = json.loads(user_info)
hobby_get = jsonObject.get("hobby"))
hobby_dict = jsonObject["hobby"]
## 둘 다 리스트 데이터가 추출되었기 때문에 아래와 같이 데이터 출력 가능
for i in range(0, len(hobby_dict)):
print(hobby_get[i], hobby_dict[i])
## 실행 결과
yoga yoga
pilates pilates
game game
복잡한 구조의 json 파일 파싱하기
위와 같이 간단한 구조의 파일이라면 구조 파악도 쉽고 추출도 별로 어렵지 않아서 좋겠지만 실제로 마주하는 json 파일은... 아래와 같이 복잡한 구조인데요! 여기서 필요한 정보만을 추출해보겠습니다.
추출하고 싶은 정보는 빨간색 글씨로 표현했습니다.
※아래 json 파일은 첨부한 '104.json' 파일입니다. 참고해주세요!
{
"이미지 정보": {
"이미지 식별자": 104,
"이미지 높이": 800,
"이미지 파일명": "Dahong_422_00_HS_마름모핫피스BL.jpg",
"이미지 너비": 800
},
"데이터셋 정보":
{
"파일 생성일자": "2020-08-03 01:52:29",
"데이터셋 상세설명": {
"렉트좌표": {
"아우터": [ {} ],
"하의": [ {} ],
"원피스": [ {} ],
"상의": [
{
"X좌표": 231.5,
"Y좌표": 89.5,
"가로": 454,
"세로": 580
}
]
},
"폴리곤좌표": {
"아우터": [ {} ],
"하의": [ {} ],
"원피스": [ {} ],
"상의": [
{
# X, Y좌표 쌍
}
]
},
"라벨링": {
"스타일": [
{
"스타일": "레트로",
"서브스타일": "오리엔탈"
}
],
"아우터": [ {} ],
"하의": [ {} ],
"원피스": [ {} ],
"상의": [
{
"기장": "노멀",
"색상": "그린",
"서브색상": "실버",
"카테고리": "블라우스",
"디테일": [ "플리츠", "비즈" ],
"소매기장": "긴팔",
"소재": [ "시폰" ],
"프린트": [ "그래픽" ],
"넥라인": "라운드넥",
"핏": "루즈"
}
]
}
},
"파일 번호": 104,
"파일 이름": "Dahong_422_00_HS_마름모핫피스BL.jpg"
}}
아래 코드는 위 json 파일에서 아우터, 하의, 원피스, 상의 항목의 렉트좌표, 폴리곤 좌표, 카테고리 정보를 추출한 코드입니다.
폴리곤 좌표 데이터의 경우 {X좌표1: 1.0, Y좌표2: 1.0, X좌표3: 1.1, X좌표4: 2.0 ...} 이런 식으로 X좌표와 Y좌표가 랜덤 순서로 섞여 있어, 폴리관 좌표 쌍의 개수를 구해 (X좌표, Y좌표) 형태로 출력했습니다.
# 아우터, 하의, 원피스, 상의별로 각각 렉트좌표, 폴리곤 좌표, 카테고리 추출 코드
import json
import csv
with open('104.json', encoding='utf-8', errors='ignore') as json_data:
dataset_info = json.load(json_data)
dataset_info = dataset_info['데이터셋 정보']['데이터셋 상세설명']
ploygon_points = dataset_info['폴리곤좌표']
labels = dataset_info['라벨링']
# 아우터, 하의, 원피스, 상의 별로 각 객체의 렉트좌표, 폴리곤 좌표, 카테고리 추출
# 카테고리 정보가 없을 경우에는 '카테고리 항목 없음' 출력
main_category = ['아우터', '하의', '원피스', '상의']
for i in main_category:
print("-- < 현재 항목 > : {} -- ".format(i))
polygon_points = ploygon_points[i][0]
#print(polygon_points)
points = []
for j in range(0, int(len(polygon_points)/2)):
x = polygon_points.get("X좌표"+str(j+1))
y = polygon_points.get("Y좌표"+str(j+1))
xy = x, y
points.append(xy)
print("★포인트 좌표★:", points)
label = labels[i][0]
try:
label = label['카테고리']
## bbox
rect_ = dataset_info["렉트좌표"]
rect_ = rect_[i][0]
#print(rect_)
x = rect_['X좌표']
y =rect_['Y좌표']
w =rect_['가로']
h =rect_['세로']
bbox = [[x, y],
[x + w, y],
[x + w, y + h],
[x, y + h]]
#print(bbox)
except KeyError:
print("카테고리 항목 정보 존재X")
## 실행 결과
"""
-- < 현재 항목 > : 아우터 --
★포인트 좌표★: []
카테고리 항목 정보 존재X
-- < 현재 항목 > : 하의 --
★포인트 좌표★: []
카테고리 항목 정보 존재X
-- < 현재 항목 > : 원피스 --
★포인트 좌표★: []
카테고리 항목 정보 존재X
-- < 현재 항목 > : 상의 --
{'X좌표39': 680.0, 'X좌표38': 658.0, 'X좌표37': 634.0, 'X좌표36': 615.0, 'X좌표35': 620.0, 'X좌표34': 669.0, 'X좌표33': 664.0, 'X좌표32': 644.0, 'X좌표31': 650.0, 'X좌표30': 648.0, 'X좌표49': 579.0, 'X좌표48': 597.0, 'X좌표47': 607.0, 'X좌표46': 614.0, 'X좌표45': 610.0, 'X좌표44': 621.0, 'X좌표43': 628.0, 'X좌표42': 621.0, 'X좌표41': 639.0, 'X좌표40': 686.0, 'Y좌표26': 640.0, 'Y좌표25': 644.0, 'Y좌표28': 610.0, 'Y좌표27': 622.0, 'Y좌표22': 652.0, 'Y좌표21': 658.0, 'Y좌표24': 631.0, 'Y좌표23': 638.0, 'Y좌표20': 652.0, 'X좌표59': 370.0, 'X좌표58': 383.0, 'X좌표57': 407.0, 'X좌표56': 434.0, 'X좌표55': 458.0, 'X좌표54': 488.0, 'X좌표53': 498.0, 'X좌표52': 508.0, 'X좌표51': 531.0, 'X좌표50': 549.0, 'Y좌표29': 619.0, 'Y좌표15': 662.0, 'Y좌표14': 646.0, 'Y좌표17': 665.0, 'Y좌표16': 670.0, 'Y좌표11': 623.0, 'Y좌표10': 600.0, 'Y좌표13': 630.0, 'Y좌표12': 630.0, 'Y좌표19': 642.0, 'Y좌표18': 654.0, 'X좌표60': 534.0, 'Y좌표48': 131.0, 'Y좌표47': 168.0, 'Y좌표49': 111.0, 'Y좌표44': 308.0, 'Y좌표43': 344.0, 'Y좌표46': 234.0, 'Y좌표45': 288.0, 'Y좌표40': 475.0, 'Y좌표42': 378.0, 'Y좌표41': 405.0, 'Y좌표37': 509.0, 'Y좌표36': 508.0, 'X좌표8': 250.0, 'Y좌표39': 492.0, 'X좌표9': 238.0, 'Y좌표38': 500.0, 'Y좌표33': 611.0, 'Y좌표32': 626.0, 'Y좌표35': 520.0, 'Y좌표34': 603.0, 'Y좌표31': 608.0, 'Y좌표30': 625.0, 'X좌표2': 286.0, 'X좌표3': 269.0, 'X좌표1': 364.0, 'X좌표6': 268.0, 'X좌표7': 266.0, 'X좌표4': 268.0, 'X좌표5': 268.0, 'Y좌표9': 588.0, 'Y좌표60': 59.0, 'X좌표19': 391.0, 'X좌표18': 385.0, 'X좌표17': 368.0, 'X좌표16': 338.0, 'X좌표15': 305.0, 'X좌표14': 282.0, 'X좌표13': 278.0, 'X좌표12': 271.0, 'X좌표11': 248.0, 'Y좌표4': 248.0, 'X좌표10': 232.0, 'Y좌표3': 172.0, 'Y좌표2': 112.0, 'Y좌표1': 90.0, 'Y좌표8': 522.0, 'Y좌표7': 466.0, 'Y좌표6': 406.0, 'Y좌표5': 336.0, 'Y좌표59': 92.0, 'Y좌표58': 109.0, 'Y좌표55': 141.0, 'Y좌표54': 135.0, 'Y좌표57': 123.0, 'Y좌표56': 144.0, 'Y좌표51': 97.0, 'Y좌표50': 107.0, 'Y좌표53': 109.0, 'Y좌표52': 95.0, 'X좌표29': 624.0, 'X좌표28': 587.0, 'X좌표27': 575.0, 'X좌표26': 559.0, 'X좌표25': 527.0, 'X좌표24': 492.0, 'X좌표23': 486.0, 'X좌표22': 464.0, 'X좌표21': 436.0, 'X좌표20': 406.0}
★포인트 좌표★: [(364.0, 90.0), (286.0, 112.0), (269.0, 172.0), (268.0, 248.0), (268.0, 336.0), (268.0, 406.0), (266.0, 466.0), (250.0, 522.0), (238.0, 588.0), (232.0, 600.0), (248.0, 623.0), (271.0, 630.0), (278.0, 630.0), (282.0, 646.0), (305.0, 662.0), (338.0, 670.0), (368.0, 665.0), (385.0, 654.0), (391.0, 642.0), (406.0, 652.0), (436.0, 658.0), (464.0, 652.0), (486.0, 638.0), (492.0, 631.0), (527.0, 644.0), (559.0, 640.0), (575.0, 622.0), (587.0, 610.0), (624.0, 619.0), (648.0, 625.0), (650.0, 608.0), (644.0, 626.0), (664.0, 611.0), (669.0, 603.0), (620.0, 520.0), (615.0, 508.0), (634.0, 509.0), (658.0, 500.0), (680.0, 492.0), (686.0, 475.0), (639.0, 405.0), (621.0, 378.0), (628.0, 344.0), (621.0, 308.0), (610.0, 288.0), (614.0, 234.0), (607.0, 168.0), (597.0, 131.0), (579.0, 111.0), (549.0, 107.0), (531.0, 97.0), (508.0, 95.0), (498.0, 109.0), (488.0, 135.0), (458.0, 141.0), (434.0, 144.0), (407.0, 123.0), (383.0, 109.0), (370.0, 92.0), (534.0, 59.0)]
"""
끝!!