Python에서 느린 JSON 처리 - '속성 이름 필요'
Pythons(2.7) 'json' 모듈을 사용하여 다양한 JSON 피드를 처리하려고 합니다.유감스럽게도 이러한 피드 중 일부는 JSON 표준을 준수하지 않습니다. 특히 일부 키는 이중 음성 기호("")로 둘러싸여 있지 않습니다.이로 인해 Python이 버그아웃됩니다.
수신 데이터를 해석하고 복구하기 위해 못생긴 코드를 작성하기 전에 Python이 이 잘못된 형식의 JSON을 해석하거나 데이터를 "복구"하여 유효한 JSON이 되도록 할 수 있는 방법이 없을까?
작업 예
import json
>>> json.loads('{"key1":1,"key2":2,"key3":3}')
{'key3': 3, 'key2': 2, 'key1': 1}
깨진 예
import json
>>> json.loads('{key1:1,key2:2,key3:3}')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\json\__init__.py", line 310, in loads
return _default_decoder.decode(s)
File "C:\Python27\lib\json\decoder.py", line 346, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Python27\lib\json\decoder.py", line 362, in raw_decode
obj, end = self.scan_once(s, idx)
ValueError: Expecting property name: line 1 column 1 (char 1)
이 특정 프로바이더로부터의 JSON을 수정하기 위해 작은 REGEX를 작성했습니다만, 이것이 향후의 문제가 될 것으로 생각됩니다.제가 생각해낸 것은 다음과 같습니다.
>>> import re
>>> s = '{key1:1,key2:2,key3:3}'
>>> s = re.sub('([{,])([^{:\s"]*):', lambda m: '%s"%s":'%(m.group(1),m.group(2)),s)
>>> s
'{"key1":1,"key2":2,"key3":3}'
JSON 파서를 사용하여 JSON이 아닌 것을 해석하려고 합니다.가장 좋은 방법은 피드를 만든 사람이 수정하도록 하는 것입니다.
그게 항상 가능한 것은 아니라는 걸 이해합니다.파손 정도에 따라 정규식을 사용하여 데이터를 수정할 수 있습니다.
j = re.sub(r"{\s*(\w)", r'{"\1', j)
j = re.sub(r",\s*(\w)", r',"\1', j)
j = re.sub(r"(\w):", r'\1":', j)
다른 옵션은 엄격하지 않은 모드에서 json을 해석할 수 있는 demjson 모듈을 사용하는 것입니다.
네드와 치즈인버트가 지적한 정규 표현은 경기가 끈 안에 있을 때 고려되지 않는다.
다음 예를 참조하십시오(치즈 인버트의 용액 사용).
>>> fixLazyJsonWithRegex ('{ key : "a { a : b }", }')
'{ "key" : "a { "a": b }" }'
문제는 예상되는 출력은 다음과 같습니다.
'{ "key" : "a { a : b }" }'
JSON 토큰은 python 토큰의 서브셋이기 때문에 python의 tokenize 모듈을 사용할 수 있습니다.
제가 틀렸다면 수정해 주세요.하지만 다음 코드는 모든 경우에 느린 json 문자열을 수정해 줍니다.
import tokenize
import token
from StringIO import StringIO
def fixLazyJson (in_text):
tokengen = tokenize.generate_tokens(StringIO(in_text).readline)
result = []
for tokid, tokval, _, _, _ in tokengen:
# fix unquoted strings
if (tokid == token.NAME):
if tokval not in ['true', 'false', 'null', '-Infinity', 'Infinity', 'NaN']:
tokid = token.STRING
tokval = u'"%s"' % tokval
# fix single-quoted strings
elif (tokid == token.STRING):
if tokval.startswith ("'"):
tokval = u'"%s"' % tokval[1:-1].replace ('"', '\\"')
# remove invalid commas
elif (tokid == token.OP) and ((tokval == '}') or (tokval == ']')):
if (len(result) > 0) and (result[-1][1] == ','):
result.pop()
# fix single-quoted strings
elif (tokid == token.STRING):
if tokval.startswith ("'"):
tokval = u'"%s"' % tokval[1:-1].replace ('"', '\\"')
result.append((tokid, tokval))
return tokenize.untokenize(result)
따라서 json 문자열을 해석하기 위해 json.loads가 실패하면 fixLazyJson에 대한 콜을 캡슐화할 수 있습니다(정확한 형식의 json에 대한 성능 저하를 피하기 위해).
import json
def json_decode (json_string, *args, **kwargs):
try:
json.loads (json_string, *args, **kwargs)
except:
json_string = fixLazyJson (json_string)
json.loads (json_string, *args, **kwargs)
lazy json을 수정할 때 나타나는 유일한 문제는 json이 잘못된 형식일 경우 두 번째 json.loads에 의해 발생한 오류가 원래 문자열의 행과 열을 참조하는 것이 아니라 수정된 문자열을 참조하는 것입니다.
마지막으로, 문자열 대신 파일 개체를 받아들이는 방법을 업데이트하는 것이 간단하다는 점을 지적하고 싶습니다.
보너스: 이와는 별도로 사람들은 보통 설정 파일에 json을 사용할 때 C/C++ 코멘트를 포함하기를 좋아합니다.이 경우 정규 표현을 사용하여 코멘트를 삭제하거나 확장 버전을 사용하여 json 문자열을 한 번에 수정할 수 있습니다.
import tokenize
import token
from StringIO import StringIO
def fixLazyJsonWithComments (in_text):
""" Same as fixLazyJson but removing comments as well
"""
result = []
tokengen = tokenize.generate_tokens(StringIO(in_text).readline)
sline_comment = False
mline_comment = False
last_token = ''
for tokid, tokval, _, _, _ in tokengen:
# ignore single line and multi line comments
if sline_comment:
if (tokid == token.NEWLINE) or (tokid == tokenize.NL):
sline_comment = False
continue
# ignore multi line comments
if mline_comment:
if (last_token == '*') and (tokval == '/'):
mline_comment = False
last_token = tokval
continue
# fix unquoted strings
if (tokid == token.NAME):
if tokval not in ['true', 'false', 'null', '-Infinity', 'Infinity', 'NaN']:
tokid = token.STRING
tokval = u'"%s"' % tokval
# fix single-quoted strings
elif (tokid == token.STRING):
if tokval.startswith ("'"):
tokval = u'"%s"' % tokval[1:-1].replace ('"', '\\"')
# remove invalid commas
elif (tokid == token.OP) and ((tokval == '}') or (tokval == ']')):
if (len(result) > 0) and (result[-1][1] == ','):
result.pop()
# detect single-line comments
elif tokval == "//":
sline_comment = True
continue
# detect multiline comments
elif (last_token == '/') and (tokval == '*'):
result.pop() # remove previous token
mline_comment = True
continue
result.append((tokid, tokval))
last_token = tokval
return tokenize.untokenize(result)
네드의 제안에 따라 다음과 같은 것이 도움이 되었습니다.
j = re.sub(r"{\s*'?(\w)", r'{"\1', j)
j = re.sub(r",\s*'?(\w)", r',"\1', j)
j = re.sub(r"(\w)'?\s*:", r'\1":', j)
j = re.sub(r":\s*'(\w+)'\s*([,}])", r':"\1"\2', j)
비슷한 경우, 저는 .AFIK를 사용했는데, 이것은 상수가 계속 될 때만 작동되지 않습니다.null
Python (Python)에)None
계시니까null/None
하다
import ast
decoded_object= ast.literal_eval(json_encoded_text)
에 Neds를 합니다.(?!/)
url의 문제를 .
j = re.sub(r"{\s*'?(\w)", r'{"\1', j)
j = re.sub(r",\s*'?(\w)", r',"\1', j)
j = re.sub(r"(\w)'?\s*:(?!/)", r'\1":', j)
j = re.sub(r":\s*'(\w+)'\s*([,}])", r':"\1"\2', j)
j = re.sub(r",\s*]", "]", j)
언급URL : https://stackoverflow.com/questions/4033633/handling-lazy-json-in-python-expecting-property-name
'programing' 카테고리의 다른 글
Wordpress 썸네일의 이미지 폭만 설정하려면 어떻게 해야 합니까? (0) | 2023.03.27 |
---|---|
패키지를 편집합니다.명령줄의 json (0) | 2023.03.27 |
mdDialog에 데이터 전달 (0) | 2023.03.27 |
ng-messages to name Atribute를 동적으로 각도 설정 (0) | 2023.03.27 |
Gitlab CI를 사용하여 Java Maven 프로젝트를 구축하는 방법은 무엇입니까? (0) | 2023.03.27 |