JSON을 CSV로
중첩 객체와 배열을 명시적으로 처리하며, 크기 제한도 업로드도 없습니다.
붙여 넣은 것은 여러분의 브라우저를 떠나지 않습니다. connect-src 허용 목록 덕분에 이는 약속이 아니라 브라우저가 강제하는 보장입니다. 직접 확인하기
CSV는 네모나고 JSON은 나무입니다. 그래서 모든 변환기는 일련의 판단을 내려야 합니다. 대부분은 조용히 내립니다. 이 페이지는 판단을 내리고, 무엇을 골랐는지 밝히고, 바꿀 수 있게 해 둡니다.
파일 크기 제한도 하루 사용량 제한도 없습니다. 그런 걸 강제할 서버가 아예 없기 때문입니다.
열은 합집합이지, 첫 객체가 아닙니다
가장 파급이 큰 결정입니다. 0번 요소의 키만 읽는 변환기는 뒤쪽 레코드에만 나오는 필드를 조용히 버립니다. 그리고 그 사실은 보고서에서 열 하나가 사라진 한참 뒤에야 드러납니다.
이 도구는 모든 행의 모든 경로를, 각각 처음 나타난 순서대로 모읍니다. 더 느리고, 그리고 맞습니다. JavaScript에서 가장 많이 쓰이는 CSV 라이브러리인 Papa Parse는 기본적으로 첫 객체를 쓰고, 다르게 하려면 columns 옵션을 명시해야 합니다. 직접 만들 생각이라면 알아 둘 만한 점입니다.
행 안의 배열: 네 가지 정책, 하나의 기본값
여기가 진짜로 모호한 지점이고, 정답은 없으며 있는 건 올바른 기본값뿐입니다.
- 인덱스 열(기본값)
- tags.0, tags.1 식입니다. 정보를 잃지 않고 왕복도 됩니다. 긴 배열 하나가 열 개수를 폭발시키는 게 비용입니다.
- 한 셀로 합치기
- 값을 세미콜론으로 잇습니다. 읽기 좋고, 정보를 잃으며, 값에 구분자가 들어 있으면 위험합니다.
- 한 셀에 JSON 그대로
- 배열을 JSON 텍스트로 직렬화합니다. 정보를 잃지 않고 간결하지만, 뒤에서 다시 파싱해야 합니다.
- 행으로 펼치기
- 배열 요소마다 출력 행을 하나씩 만들고 스칼라 열은 반복합니다. pandas의 json_normalize record_path가 이것입니다. 일대다 관계에는 맞고 그 밖의 모든 경우에는 틀리므로, 직접 켜야 하고 지정한 경로에만 적용됩니다.
엑셀에 관해 선택의 여지가 없는 두 가지
- UTF-8 바이트 순서 표식
- 엑셀은 CSV 안의 UTF-8을 감지하지 못합니다. 파일 앞에 BOM이 없으면 바이트를 시스템 코드 페이지로 읽고, 악센트 문자와 이모지가 전부 깨져서 들어옵니다. 여기서 BOM이 기본으로 켜져 있는 이유가 바로 그것이고, BOM에 체하는 파이프라인을 위해 끄는 것도 클릭 한 번입니다.
- 수식 인젝션
- =, +, -, @ 로 시작하는 셀은 엑셀, 구글 시트, LibreOffice가 수식으로 실행합니다. 여러분이 만든 CSV 안의 =HYPERLINK("http://evil","click") 이라는 값은 다른 사람의 스프레드시트에서 살아 있는 링크가 됩니다. OWASP는 이를 CSV 인젝션이라고 부릅니다. 그런 셀에는 기본적으로 아포스트로피를 앞에 붙이고, 그렇게 했다는 사실을 도구가 알려 줍니다.
null과 빈 문자열
JSON에서 이 둘은 다른 값이고 엑셀은 둘 다 빈칸으로 보여 줍니다. 그래서 대부분의 변환기가 둘을 하나로 뭉개고 구분이 사라집니다. 여기서는 null을 따옴표 없는 빈 셀로, 빈 문자열을 따옴표가 있는 빈 셀로 만들어서 왕복이 살아남습니다. 빈 문자열 하나당 두 글자가 늘어나고, 그만한 값어치가 있습니다.
How to do this in code
코드에서의 변환, 그리고 맞고 틀림을 가르는 인자들.
py Python, pandas
encoding="utf-8-sig"가 엑셀이 필요로 하는 BOM을 pandas에서 쓰는 방법입니다. 그냥 utf-8이면 엑셀이 잘못 읽는 파일이 됩니다.
import pandas as pd
# Flatten nested objects to dotted columns
df = pd.json_normalize(records)
df.to_csv('out.csv', index=False, encoding='utf-8-sig')
# One row per element of a nested array
df = pd.json_normalize(records, record_path='items', meta=['id']) sh jq
문자열 보간 대신 @csv를 쓰세요. 따옴표 규칙을 대신 처리해 줍니다.
# Union of keys as the header, then the rows
jq -r '(map(keys) | add | unique) as $c
| $c, (.[] | [.[$c[]]])
| @csv' records.json > out.csv
# @csv quotes and escapes correctly; @text does not js JavaScript
import Papa from 'papaparse';
// Pass the union explicitly. Without it, Papa takes the keys of
// the first object and silently drops the rest.
const columns = [...new Set(records.flatMap(Object.keys))];
const csv = Papa.unparse(records, { columns }); go Go
w := csv.NewWriter(f)
w.Write(columns)
for _, rec := range records {
row := make([]string, len(columns))
for i, c := range columns {
row[i] = fmt.Sprint(rec[c])
}
w.Write(row)
}
w.Flush() 자주 묻는 질문
- 엑셀에서 CSV 글자가 깨지는 이유는?
- 파일은 UTF-8인데 엑셀이 시스템 코드 페이지로 읽었기 때문입니다. 바이트 순서 표식 옵션을 켠 채로 두세요. 스프레드시트가 아닌 다른 곳으로 가는 파일이라면 끄세요. 일부 파서는 BOM을 첫 열 이름의 일부로 취급합니다.
- 어떤 셀이 아포스트로피로 시작하는 이유는?
- =, +, -, @ 로 시작했기 때문입니다. 스프레드시트는 그걸 수식으로 실행합니다. 아포스트로피가 그걸 무력화합니다. 날 값이 필요하고 파일이 갈 곳을 믿는다면 옵션을 끄세요.
- 제 데이터가 객체 배열이 아니면요?
- 객체 하나면 한 행이 됩니다. 스칼라 배열이면 열 하나가 됩니다. {"data": [...]} 같은 감싸개는 안쪽 배열을 쓰고, 그게 규칙이 아니라 추측이기 때문에 그렇게 골랐다고 알려 줍니다.
- 유럽식 엑셀에는 어떤 구분자를 써야 하나요?
- 세미콜론입니다. 엑셀은 시스템의 목록 구분 기호에서 구분자를 가져오는데, 쉼표를 소수점으로 쓰는 로캘에서는 그게 세미콜론입니다. 독일이나 프랑스 환경에서 쉼표로 구분된 파일이 한 열로 열리는 이유가 그것입니다.