2016-08-12 5 views
1

私は解析するために非標準の「JSON」ファイルを持っています。各項目はカンマ区切りではなくセミコロン区切りです。 ;,に置き換えることはできません。値は;です。 "こんにちは世界"。これをJSONが通常解析するのと同じ構造にするにはどうすればよいですか?標準以外のセミコロンを「JSON」で区切って解析します。

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

はどのような醜態がために来たのか? –

+0

は常に ';'で区切られています。 – Julien

+0

JSONのオブジェクトは1つだけですか? –

答えて

6

カンマの代わりにセミコロンを1にテキストストリームを変換するための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)位置タプルに着目して再instatedすることができます。トークンの周りの空白はJSONパーサーにとって重要ではないので、私はこれを気にしませんでした。

0

あなたは何か奇妙なことをして、それを(おそらく)正しく得ることができます。

JSONの文字列は、あなたがあなたのJSONパーサーは、(例えば、Pythonのような)非厳格なJSONを読み込むことができる場合ので、ファイルが正しく解析されるすべての;\t,に置き換えることができ、そのような\tなどの制御文字を持つことができませんので。

この後、\t,;に置き換え、通常のJSONパーサーを使用して最終的に正しいオブジェクトを読み込むことができるように、データを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,' ';')) 
0

単純な文字ステートマシンを使用して、有効な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", 
    ... 
} 
0

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']} 
関連する問題