레이블이 yaml인 게시물을 표시합니다. 모든 게시물 표시
레이블이 yaml인 게시물을 표시합니다. 모든 게시물 표시

2022년 10월 15일 토요일

json.decoder.JSONDecodeError: Invalid control character at line XX column XX (char XX)

 

json.decoder.JSONDecodeError: Invalid control character at 오류 해결법

먼저 오류 상황을 알아보도록 하겠습니다.

import json
import yaml
code_yaml = "" \
'''
"a": "안녕하세요\n반갑습니다."
'''
code_json = "" \
'''
{
"a":"안녕하세요\n반갑습니다."
}
'''
print(code_yaml)
yaml_data = yaml.load(code_yaml, Loader=yaml.FullLoader)
print(yaml_data)

print(code_json)
json_data = json.loads(code_json)
print(json_data)

위와 같은 경우 실행을 시키면 아래와 같은 형태의 오류가 발생합니다.

Traceback (most recent call last):
  File "C:/Users/jun/Documents/GitHub/sourcecode/python/example/_40_yaml_json/json_load_decoder_exception.py", line 18, in <module>
    json_data = json.loads(code_json)
  File "C:\Users\jun\AppData\Local\Programs\Python\Python38\lib\json\__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "C:\Users\jun\AppData\Local\Programs\Python\Python38\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Users\jun\AppData\Local\Programs\Python\Python38\lib\json\decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Invalid control character at: line 3 column 18 (char 27)

오류가 발생하는 사유는 

https://docs.python.org/ko/3/library/json.html#json.JSONDecoder

strict가 거짓이면 (True가 기본값입니다), 문자열 안에 제어 문자가 허용됩니다. 이 문맥에서 제어 문자는 0–31 범위의 문자 코드를 가진 것들인데, '\t' (탭), '\n', '\r' 및 '\0'을 포함합니다.

기본적으로 제어 문자는 json 문자에서는 표현이 안된다고 합니다. 이것을 해결하기 위해서는 strict 옵션을 사용해야 하는데 아래는 사용 예제입니다.

import json
code = "" \
'''
{
"a":"안녕하세요\n반갑습니다."
}
'''
print(code)

json_data = json.loads(code, strict=False)
print(json_data)

아주 간단합니다.

아래는 실행 결과 입니다.

{
"a": "안녕하세요
반갑습니다."
}

{'a': '안녕하세요\n반갑습니다.'}


참고로 이 현상은 yaml 에서는 발생하지 않습니다.

제일 앞에 예제에서 yaml 부분과 같이 실행한 코드가 있습니다.



2022년 10월 8일 토요일

python dict type을 yaml, json으로 저장할때 한글 처리(unicode 처리)

 앞서 json 을 yaml 변환하는 코드를 소개해 드렸는데요

실제로 사용해보니 한글 처리에 문제가 발생하였습니다

즉 기존 코드에 한글이 있게 되면 escape 코드 형태로 출력이 되었습니다.


dict type을 yaml, json에 한글 그대로 저장하는 방법


기존 예제

python script (convert) json 파일을 yaml  파일로 변환

https://swlock.blogspot.com/2022/10/python-script-convert-json-yaml.html

위의 예제에서 한글을 사용하게 되면 아래와 같은 형태가 됩니다.


a1:
- "\uD55C\uAE00"
- " \uC138\uC885 "
- "\uB300\uC655"
a2:
- 2
- '3'

원하는 결과 물은 아래와 같습니다.


a1:
- 한글
- ' 세종 '
- 대왕
a2:
- 2
- '3'


핵심 코드는 아래와 같습니다.

if format == 'json':
json.dump(data, fp, ensure_ascii=False)
elif format == 'yaml':
yaml.dump(data, fp, allow_unicode=True)

json dump에서는 ensure_ascii=False 를 사용하는 것이고 yaml dump에서는 allow_unicode=True를 사용하면 됩니다.


아래는 완성된 코드와 테스트 결과 입니다.

import json
import yaml # pip install pyyaml
import os


def write_file(filename, data):
format = ''
if filename.endswith(".json"):
format = "json"
if filename.endswith(".yaml"):
format = "yaml"
with open(filename, 'w', encoding='utf-8') as fp:
if format == 'json':
json.dump(data, fp, ensure_ascii=False)
elif format == 'yaml':
yaml.dump(data, fp, allow_unicode=True)
else:
fp.write(data)
fp.close()


def read_file(filename):
format = ''
if filename.endswith(".json"):
format = "json"
if filename.endswith(".yaml"):
format = "yaml"
with open(filename, 'r', encoding='utf-8') as fp:
if format == 'json':
ret_data = json.load(fp)
elif format == 'yaml':
ret_data = yaml.load(fp, Loader=yaml.FullLoader)
else:
ret_data = fp.read()
fp.close()
return ret_data
return None


if __name__ == "__main__":
# make test file
a = {"a1": ["한글", " 세종 ", "대왕"], "a2": [2, "3"]}
write_file("a.json", a)

b = {"b1": 1, "b2": [2, '만세"만세', '만만세', '공백 ', ' 공백', '']}
write_file("b.json", b)

print(a)
print(b)

want_to_folder_name = "." # 이곳에 원하는 폴더명을 넣으세요
dirListing = os.listdir(want_to_folder_name)
for filename in dirListing:
if filename.endswith(".json"):
json_data = read_file(filename)
replaced = filename.replace(".json", ".yaml")
write_file(replaced, json_data)
print(f"convert:{filename}->{replaced}")

print(read_file("a.yaml"))
print(read_file("b.yaml"))


# {'a1': ['한글', ' 세종 ', '대왕'], 'a2': [2, '3']}
# {'b1': 1, 'b2': [2, '만세"만세', '만만세', '공백 ', ' 공백', '']}
# convert:a.json->a.yaml
# convert:b.json->b.yaml
# convert:json_test.json->json_test.yaml
# {'a1': ['한글', ' 세종 ', '대왕'], 'a2': [2, '3']}
# {'b1': 1, 'b2': [2, '만세"만세', '만만세', '공백 ', ' 공백', '']}





2022년 10월 2일 일요일

python script (convert) json 파일을 yaml 파일로 변환

 앞에서 yaml 에 대해서 간단하게 다룬적이 있습니다. python dict type을 쉽게 yaml로 변환해서 저장이 가능하다는 것입니다. 

그런데 막상 사용해보니 기존 프로젝트의 데이터를 변경해야 하는 경우가 많았습니다. json 파일은 많은데 변환툴을 만들어야겠다고 판단해서 이글을 작성합니다.


yaml 관련 내용은 아래 링크 참고 부탁드립니다.

https://swlock.blogspot.com/2022/04/yaml-json.html


이전 내용을 간단하게 요약하자면 ymal이나 json을 write할때는 이름과 dict 변수를 넘겨주면 됩니다.

def write_file(filename, data):
format = ''
if filename.endswith(".json"):
format = "json"
if filename.endswith(".yaml"):
format = "yaml"
with open(filename, 'w', encoding='utf-8') as fp:
if format == 'json':
json.dump(data, fp)
elif format == 'yaml':
yaml.dump(data, fp)
else:
fp.write(data)
fp.close()

읽을 때는 파일 이름만 넣어주면 됩니다.

def read_file(filename):
format = ''
if filename.endswith(".json"):
format = "json"
if filename.endswith(".yaml"):
format = "yaml"
with open(filename, 'r', encoding='utf-8') as fp:
if format == 'json':
ret_data = json.load(fp)
elif format == 'yaml':
ret_data = yaml.load(fp, Loader=yaml.FullLoader)
else:
ret_data = fp.read()
fp.close()
return ret_data
return None

추가로 여기에서 할 일은 이미 json 파일이 있는 경우 변환을 위한 스크립트가 필요합니다.

테스트를 위해서 임으로 a.json, b.json 파일을 만들었습니다.

if __name__ == "__main__":
# make test file
a = {"a1": [1, 2, 3], "a2": 2}
write_file("a.json", a)

b = {"b1": 1, "b2": [2, 3, 4]}
write_file("b.json", b)

핵심은 아래 코드 입니다.

want_to_folder_name = "."  # 이곳에 원하는 폴더명을 넣으세요
dirListing = os.listdir(want_to_folder_name)
for filename in dirListing:
if filename.endswith(".json"):
json_data = read_file(filename)
replaced = filename.replace(".json", ".yaml")
write_file(replaced, json_data)
print(f"convert:{filename}->{replaced}")

os.listdir에서 파일 목록을 얻은뒤 확장자만 변경해서 write_file 만 해주면 간단하게 처리가 가능합니다.


전체 소스 코드

import json
import yaml # pip install pyyaml
import os


def write_file(filename, data):
format = ''
if filename.endswith(".json"):
format = "json"
if filename.endswith(".yaml"):
format = "yaml"
with open(filename, 'w', encoding='utf-8') as fp:
if format == 'json':
json.dump(data, fp)
elif format == 'yaml':
yaml.dump(data, fp)
else:
fp.write(data)
fp.close()


def read_file(filename):
format = ''
if filename.endswith(".json"):
format = "json"
if filename.endswith(".yaml"):
format = "yaml"
with open(filename, 'r', encoding='utf-8') as fp:
if format == 'json':
ret_data = json.load(fp)
elif format == 'yaml':
ret_data = yaml.load(fp, Loader=yaml.FullLoader)
else:
ret_data = fp.read()
fp.close()
return ret_data
return None


if __name__ == "__main__":
# make test file
a = {"a1": [1, 2, 3], "a2": 2}
write_file("a.json", a)

b = {"b1": 1, "b2": [2, 3, 4]}
write_file("b.json", b)

want_to_folder_name = "." # 이곳에 원하는 폴더명을 넣으세요
dirListing = os.listdir(want_to_folder_name)
for filename in dirListing:
if filename.endswith(".json"):
json_data = read_file(filename)
replaced = filename.replace(".json", ".yaml")
write_file(replaced, json_data)
print(f"convert:{filename}->{replaced}")

여기에서 사용된 a.json


{"a1": [1, 2, 3], "a2": 2}

생성된 a.yaml


a1:
- 1
- 2
- 3
a2: 2

여기에서 사용된 b.json


{"b1": 1, "b2": [2, 3, 4]}

생성된 b.yaml


b1: 1
b2:
- 2
- 3
- 4


2022년 4월 23일 토요일

YAML 이건 꼭 써야됩니다. 아직도 안쓰는 분 있다면 적극 추천 (json 대체)

YAML 소개입니다.

예전에 XML을 많이 사용했었죠? 그러다가 XML을 사용하다보면 뭔가 복잡하고 쓰기도 힘들고  많은 부분에서 json 을 사용했습니다. 

그런데 json도 많이 사용하다보니 문제가 발생합니다. json을 많이 작성하다보면 주석이 안됩니다. data 저장용이나 설정값으로 많이 사용하는데 주석이 안되니 엄청 불편합니다. 일일이 다른곳에 설명서를 만들어야 합니다.


1. 기본

YAML이 뭔데 추천하는지 알아보도록 하겠습니다.

알아둬야 하는 기본적인 내용이 있는데 python과 비슷하게 들여쓰기를 이용한 하위 구조체를 만듭니다.

1.1. 공백 문자를 이용한 들여쓰기로 구조체를 구분한다. 그러나 탭문자를 들여쓰기에 사용하지 않는다.

1.2. - 는 list( - 뒤 공백 필요)

1.3. 값: hash즉 python용어로는 dict 형태( : 뒤 공백 필요 )

1.4. --- 블록을 의미함

같은 내용을 JSON, YAML 예제로 사용해보면 쉽게 이해가 갈겁니다.


2. 예제

https://www.json2yaml.com/ 여기에서 변환이 가능합니다.


json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
  "json": [
    "rigid",
    "better for data interchange"
  ],
  "yaml": [
    "slim and flexible",
    "better for configuration"
  ],
  "object": {
    "key": "value",
    "array": [
      {
        "null_value": null
      },
      {
        "boolean": true
      },
      {
        "integer": 1
      },
      {
        "alias": "aliases are like variables"
      },
      {
        "alias": "aliases are like variables"
      }
    ]
  },
  "paragraph": "Blank lines denote \nparagraph breaks\n",
  "content": "Or we\ncan auto\nconvert line breaks\nto save space",
  "alias": {
    "bar": "baz"
  },
  "alias_reuse": {
    "bar": "baz"
  }
}


yaml - _40_yaml_data.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 주석
json:
- rigid
- better for data interchange
yaml:
- slim and flexible
- better for configuration
object:
  key: value
  array:
  - null_value:
  - boolean: true
  - integer: 1
  - alias: aliases are like variables
  - alias: aliases are like variables
paragraph: "Blank lines denote \nparagraph breaks\n"
content: |-
  Or we
  can auto
  convert line breaks
  to save space
alias:
  bar: baz
alias_reuse:
  bar: baz


text 입력시 기본적으로는 앞뒤 공백이 사라지니 주의가 필요하나 "" 따옴표로 묶으면 공백도 포함하게 됩니다. 기본적으로는 따옴표를 사용하지 않아도 됩니다.


3. python에서 yaml 사용

json 과 yaml을 동시에 read/write가능한 함수를 만들었습니다.

python에서 yaml 을 사용하려면 pip install pyyaml 로 패키지를 설치하도록 합니다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import json
import yaml # pip install pyyaml
import os

def write_file(filename, data):
	format = ''
	if filename.endswith(".json"):
		format = "json"
	if filename.endswith(".yaml"):
		format = "yaml"
	if filename.endswith(".yml"):
		format = "yml"
	with open(filename, 'w', encoding='utf-8') as fp:
		if format == 'json':
			json.dump(data, fp)
		elif format == 'yaml':
			yaml.dump(data, fp)
		else:
			fp.write(data)
		fp.close()

def write_json(filename, data):
	with open(filename, 'w', encoding='utf-8') as fp:
		json.dump(data, fp)
		fp.close()

def read_file(filename):
	format = ''
	if filename.endswith(".json"):
		format = "json"
	if filename.endswith(".yaml"):
		format = "yaml"
	if filename.endswith(".yml"):
		format = "yml"
	with open(filename, 'r', encoding='utf-8') as fp:
		if format == 'json':
			ret_data = json.load(fp)
		elif format == 'yaml':
			ret_data = yaml.load(fp,Loader=yaml.FullLoader)
		else:
			ret_data = fp.read()
		fp.close()
		return ret_data
	return None

if __name__ == "__main__":
	data = read_file("_40_yaml_data.yaml")
	print("Loaded")
	print(data)
	write_file("json_test.json",data)
	data2 = read_file("json_test.json")
	print("Loaded2")
	print(data2)
	write_file("json_test.yaml",data)
	
'''

# output

Loaded
{'json': ['rigid', 'better for data interchange'], 'yaml': ['slim and flexible', 'better for configuration'], 'object': {'key': 'value', 'array': [{'null_value': None}, {'boolean': True}, {'integer': 1}, {'alias': 'aliases are like variables'}, {'alias': 'aliases are like variables'}]}, 'paragraph': 'Blank lines denote \nparagraph breaks\n', 'content': 'Or we\ncan auto\nconvert line breaks\nto save space', 'alias': {'bar': 'baz'}, 'alias_reuse': {'bar': 'baz'}}
Loaded2
{'json': ['rigid', 'better for data interchange'], 'yaml': ['slim and flexible', 'better for configuration'], 'object': {'key': 'value', 'array': [{'null_value': None}, {'boolean': True}, {'integer': 1}, {'alias': 'aliases are like variables'}, {'alias': 'aliases are like variables'}]}, 'paragraph': 'Blank lines denote \nparagraph breaks\n', 'content': 'Or we\ncan auto\nconvert line breaks\nto save space', 'alias': {'bar': 'baz'}, 'alias_reuse': {'bar': 'baz'}}

'''


확장자를 이용하여 yaml, json을 구별해서 처리하도록 되어있습니다.

위의 예제에 사용하는 예제는 앞서 설명드린 yaml - _40_yaml_data.yaml 사용하였습니다.