解析非标准的分号“JSON”

问题描述:

我有一个非标准的“JSON”文件来解析。每个项目都用分号分隔,而不是用逗号分隔。我不能简单地用,替换;,因为可能有一些值包含;,例如。 “你好,世界”。我该如何解析这个JSON通常解析它的结构?解析非标准的分号“JSON”

{ 
    "client" : "someone"; 
    "server" : ["s1"; "s2"]; 
    "timestamp" : 1000000; 
    "content" : "hello; world"; 
    ... 
} 
+9

怎么这样的憎恶来呢? –

+0

是分隔';'总是在行尾? – Julien

+0

它只是JSON中的单个对象吗? –

使用Python tokenize module将文本流转换为以逗号代替分号的文本流。 Python标记器也很乐意处理JSON输入,甚至包括分号。标记生成器呈现的字符串作为整个令牌和“原始”分号流单token.OP令牌,可以更换:

import tokenize 
import json 

corrected = [] 

with open('semi.json', 'r') as semi: 
    for token in tokenize.generate_tokens(semi.readline): 
     if token[0] == tokenize.OP and token[1] == ';': 
      corrected.append(',') 
     else: 
      corrected.append(token[1]) 

data = json.loads(''.join(corrected)) 

这假定格式变得有效的JSON一旦你更换带逗号的分号;例如在结束]}之前没有尾随逗号,但如果下一个非新行标记是右大括号,您甚至可以跟踪最后添加的逗号并将其删除。

演示:

>>> import tokenize 
>>> import json 
>>> open('semi.json', 'w').write('''\ 
... { 
... "client" : "someone"; 
... "server" : ["s1"; "s2"]; 
... "timestamp" : 1000000; 
... "content" : "hello; world" 
... } 
... ''') 
>>> corrected = [] 
>>> with open('semi.json', 'r') as semi: 
...  for token in tokenize.generate_tokens(semi.readline): 
...   if token[0] == tokenize.OP and token[1] == ';': 
...    corrected.append(',') 
...   else: 
...    corrected.append(token[1]) 
... 
>>> print ''.join(corrected) 
{ 
"client":"someone", 
"server":["s1","s2"], 
"timestamp":1000000, 
"content":"hello; world" 
} 
>>> json.loads(''.join(corrected)) 
{u'content': u'hello; world', u'timestamp': 1000000, u'client': u'someone', u'server': [u's1', u's2']} 

间令牌空白被放弃了,而是开始重视tokenize.NL令牌和(lineno, start)(lineno, end)位置的元组是每个令牌的一部分可以重新设置。由于标记周围的空白对JSON解析器无关紧要,所以我没有为此烦恼。

你可以做一些奇怪的事情,并得到它(可能)是正确的。

因为JSON字符串不能有控制字符,如\t,你可以取代每;\t,因此该文件将被正确解析,如果你的JSON解析器能够装载非严格JSON(如Python的)。

之后,您只需要将数据转换回JSON,以便将所有这些\t,替换为;,然后使用常规JSON解析器最终加载正确的对象。

Python中的一些示例代码:

data = '''{ 
    "client" : "someone"; 
    "server" : ["s1"; "s2"]; 
    "timestamp" : 1000000; 
    "content" : "hello; world" 
}''' 

import json 
dec = json.JSONDecoder(strict=False).decode(data.replace(';', '\t,')) 
enc = json.dumps(dec) 
out = json.loads(dec.replace('\\t,' ';')) 

使用一个简单的人物状态机,可以将此文本转换回有效的JSON。我们需要处理的基本事情是确定当前的“状态”(无论我们是逃避字符,字符串,列表,字典等),并替换';'按','处于某种状态。

我不知道这是否是正确的写法,可能有一种方法可以缩短它,但我没有足够的编程技巧来为此创建最佳版本。

我想发表评论,就像我可以:

def filter_characters(text): 
    # we use this dictionary to match opening/closing tokens 
    STATES = { 
     '"': '"', "'": "'", 
     "{": "}", "[": "]" 
    } 

    # these two variables represent the current state of the parser 
    escaping = False 
    state = list() 

    # we iterate through each character 
    for c in text: 
     if escaping: 
      # if we are currently escaping, no special treatment 
      escaping = False 
     else: 
      if c == "\\": 
       # character is a backslash, set the escaping flag for the next character 
       escaping = True 
      elif state and c == state[-1]: 
       # character is expected closing token, update state 
       state.pop() 
      elif c in STATES: 
       # character is known opening token, update state 
       state.append(STATES[c]) 
      elif c == ';' and state == ['}']: 
       # this is the delimiter we want to change 
       c = ',' 
     yield c 

    assert not state, "unexpected end of file" 

def filter_text(text): 
    return ''.join(filter_characters(text)) 

测试用:

{ 
    "client" : "someone"; 
    "server" : ["s1"; "s2"]; 
    "timestamp" : 1000000; 
    "content" : "hello; world"; 
    ... 
} 

返回:

{ 
    "client" : "someone", 
    "server" : ["s1"; "s2"], 
    "timestamp" : 1000000, 
    "content" : "hello; world", 
    ... 
} 

Pyparsing可以很容易地编写一个字符串变压器。为要更改的字符串编写一个表达式,并添加一个分析操作(分析时回调),以便将所匹配的文本替换为您想要的内容。如果您需要避免某些情况(如引用的字符串或注释),请将其包含在扫描仪中,但请保持不变。然后,要实际转换字符串,请拨打scanner.transformString

(从你的例子中不清楚你是否可能在括号内的一个括号内的最后一个元素后面有';',所以我添加了一个术语来抑制这些,因为括号内的尾部','名单也无效JSON)

sample = """ 
{ 
    "client" : "someone"; 
    "server" : ["s1"; "s2"]; 
    "timestamp" : 1000000; 
    "content" : "hello; world"; 
}""" 


from pyparsing import Literal, replaceWith, Suppress, FollowedBy, quotedString 
import json 

SEMI = Literal(";") 
repl_semi = SEMI.setParseAction(replaceWith(',')) 
term_semi = Suppress(SEMI + FollowedBy('}')) 
qs = quotedString 

scanner = (qs | term_semi | repl_semi) 
fixed = scanner.transformString(sample) 
print(fixed) 
print(json.loads(fixed)) 

打印:

{ 
    "client" : "someone", 
    "server" : ["s1", "s2"], 
    "timestamp" : 1000000, 
    "content" : "hello; world"} 
{'content': 'hello; world', 'timestamp': 1000000, 'client': 'someone', 'server': ['s1', 's2']}