2013-08-29 27 views
12

2gis APIから、次のJSON文字列を取得しました。無効なJSON文字列を自動的に修正するにはどうすればよいですか?

{ 
    "api_version": "1.3", 
    "response_code": "200", 
    "id": "3237490513229753", 
    "lon": "38.969916127827", 
    "lat": "45.069889625267", 
    "page_url": null, 
    "name": "ATB", 
    "firm_group": { 
     "id": "3237499103085728", 
     "count": "1" 
    }, 
    "city_name": "Krasnodar", 
    "city_id": "3237585002430511", 
    "address": "Turgeneva, 172/1", 
    "create_time": "2008-07-22 10:02:04 07", 
    "modification_time": "2013-08-09 20:04:36 07", 
    "see_also": [ 
     { 
      "id": "3237491513434577", 
      "lon": 38.973110606808, 
      "lat": 45.029031222211, 
      "name": "Advance", 
      "hash": "5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e", 
      "ads": { 
       "sponsored_article": { 
        "title": "Center "ADVANCE"", 
        "text": "Business.English." 
       }, 
       "warning": null 
      } 
     } 
    ] 
} 

しかし、Pythonはそれを認識しません。

json.loads(firm_str) 

期待し、区切り文字:行1列3646(CHAR 3645)

それは引用符での問題のように見えます: "title": "センター" ADVANCE ""

どうすれば自動化することができますか? ically Pythonで?

+4

これはエンコードの問題であり、JSONの問題ではありません。 – Gijs

+0

エンコードが正しいです。奇妙な文字に注意を払わないでください –

+0

これを特定の小さな例に分けることはできますか?破損したビットが残るまでピースを取り外します。 – Joe

答えて

20

@Michaelの答えは、私には素晴らしいアイデアではありませんでしたが、少なくともあなたの例ではうまくいくようです:JSON文字列を解析してみましょう。失敗した場合は、文字そこでは例外文字列で失敗し、その文字を置き換えます。

while True: 
    try: 
     result = json.loads(s) # try to parse... 
     break     # parsing worked -> exit loop 
    except Exception as e: 
     # "Expecting , delimiter: line 34 column 54 (char 1158)" 
     # position of unexpected character after '"' 
     unexp = int(re.findall(r'\(char (\d+)\)', str(e))[0]) 
     # position of unescaped '"' before that 
     unesc = s.rfind(r'"', 0, unexp) 
     s = s[:unesc] + r'\"' + s[unesc+1:] 
     # position of correspondig closing '"' (+2 for inserted '\') 
     closg = s.find(r'"', unesc + 2) 
     s = s[:closg] + r'\"' + s[closg+1:] 
print result 

あなたは(文字列の文字があるとして多くの繰り返しとして最大で、例えば)無限ループで終わるからこれを防ぐために、いくつかの追加のチェックを追加することができます。 また、正しくない "の後にカンマがついていて、@gnibblerが指摘しているように、これは動作しません。

更新:これはエスケープ"がコンマに続いて、またはそれはそうな苦情を取得します。この場合のように、ブラケットを閉じていても、(まだ完璧ではないが)、今もかなりを動作するようですそれ以降の構文エラーについて(期待されるプロパティ名など)、最後に"までトレースします。また、対応する閉じる"を自動的にエスケープします(あると仮定して)。

+0

素晴らしいです。これはすべての私の問題を解決するわけではありませんが、それは良いスタートです。 – eandersson

+0

@eandersson「より完全なソリューション」がどのように見えるか、具体的にお考えですか?私のソリューションがうまくいかないケースは何ですか? –

+0

私は主に、これらのタイプの奇妙さを扱うライブラリを指していましたが、これが処理できない極端なケースの問題を修正するためのより柔軟なソリューションです。一例として、二重引用符を1つ追加した文字列があります。 – eandersson

6

これがAPIが返すものであれば、APIに問題があります。これは無効なJSONです。特にこの地域の周辺:

"ads": { 
      "sponsored_article": { 
       "title": "Образовательный центр "ADVANCE"", <-- here 
       "text": "Бизнес.Риторика.Английский язык.Подготовка к школе.Подготовка к ЕГЭ." 
      }, 
      "warning": null 
     } 

ADVANCE周辺の二重引用符はエスケープされません。あなたはそれを検証するためにhttp://jsonlint.com/のようなものを使って知ることができます。

これは、"がエスケープされていないため問題です。データが入手できない場合は、ソースでデータが不良です。彼らはそれを修正する必要があります。

Parse error on line 4: 
...азовательный центр "ADVANCE"",   
-----------------------^ 
Expecting '}', ':', ',', ']' 

これは、問題が修正されています。

"title": "Образовательный центр \"ADVANCE\"", 
+0

私はこれを自動的に修正する必要があります。 –

+6

「2gis」に書き込んで、正しいJSONを返さないことを伝える必要があります。 – Joe

+0

JSONを解析し、失敗した場合は 'jsonString.replace( '" "'、 '\"' ') 'を試すことができます。それはうまくいくかもしれない。しかし、他の入力に失敗する可能性があります。正しい解決策はありません。 – Joe

2

次のようにあなたは、JSON文字列に二重引用符をエスケープする必要があります。

"title": "Образовательный центр \\"ADVANCE\\"", 

プログラムでそれを修正するには、最も簡単な方法は、になりますJSONパーサーを修正して、エラーのコンテキストを取得してから、修復を試みます。

3

唯一の実際の解決策は、APIを修正する2gisです。

その間に、ひどくエンコードされたJSONエスケープする二重引用符を文字列内に修正することは可能です。すべてのキーと値のペアは改行が続く場合(ポストされたデータからのようですと)次の関数は、仕事をする:

def fixjson(badjson): 
    s = badjson 
    idx = 0 
    while True: 
     try: 
      start = s.index('": "', idx) + 4 
      end1 = s.index('",\n',idx) 
      end2 = s.index('"\n', idx) 
      if end1 < end2: 
       end = end1 
      else: 
       end = end2 
      content = s[start:end] 
      content = content.replace('"', '\\"') 
      s = s[:start] + content + s[end:] 
      idx = start + len(content) + 6 
     except: 
      return s 

してください、いくつかのassumtionsがなされていることに注意してください。

関数は、値の文字列の中の二重引用符をエスケープしてキーと値のペアに割り当てようとします。

テキストがエスケープすることが想定されるがシーケンス

": " 

後に始まり、シーケンスの前に終了

",\n 

または

"\n 

に投稿されたJSONを渡します関数はこの戻り値になります

{ 
    "api_version": "1.3", 
    "response_code": "200", 
    "id": "3237490513229753", 
    "lon": "38.969916127827", 
    "lat": "45.069889625267", 
    "page_url": null, 
    "name": "ATB", 
    "firm_group": { 
     "id": "3237499103085728", 
     "count": "1" 
    }, 
    "city_name": "Krasnodar", 
    "city_id": "3237585002430511", 
    "address": "Turgeneva, 172/1", 
    "create_time": "2008-07-22 10:02:04 07", 
    "modification_time": "2013-08-09 20:04:36 07", 
    "see_also": [ 
     { 
      "id": "3237491513434577", 
      "lon": 38.973110606808, 
      "lat": 45.029031222211, 
      "name": "Advance", 
      "hash": "5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e", 
      "ads": { 
       "sponsored_article": { 
        "title": "Center \"ADVANCE\"", 
        "text": "Business.English." 
       }, 
       "warning": null 
      } 
     } 
    ] 
} 

必要が完全に満たされていない場合は、簡単に機能をカスタマイズできます。

1

上記のアイデアは良いですが、私はそれに問題がありました。私のjson Stingはそれに1つだけの二重引用符を追加しました。 そこで、私は上記のコードを修正しました。

jsonStrは、次のように修正がある

{ 
    "api_version": "1.3", 
    "response_code": "200", 
    "id": "3237490513229753", 
    "lon": "38.969916127827", 
    "lat": "45.069889625267", 
    "page_url": null, 
    "name": "ATB", 
    "firm_group": { 
     "id": "3237499103085728", 
     "count": "1" 
    }, 
    "city_name": "Krasnodar", 
    "city_id": "3237585002430511", 
    "address": "Turgeneva, 172/1", 
    "create_time": "2008-07-22 10:02:04 07", 
    "modification_time": "2013-08-09 20:04:36 07", 
    "see_also": [ 
     { 
      "id": "3237491513434577", 
      "lon": 38.973110606808, 
      "lat": 45.029031222211, 
      "name": "Advance", 
      "hash": "5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e", 
      "ads": { 
       "sponsored_article": { 
        "title": "Center "ADVANCE", 
        "text": "Business.English." 
       }, 
       "warning": null 
      } 
     } 
    ] 
} 

た:

import json, re 
def fixJSON(jsonStr): 
    # Substitue all the backslash from JSON string. 
    jsonStr = re.sub(r'\\', '', jsonStr) 
    try: 
     return json.loads(jsonStr) 
    except ValueError: 
     while True: 
      # Search json string specifically for '"' 
      b = re.search(r'[\w|"]\s?(")\s?[\w|"]', jsonStr) 

      # If we don't find any the we come out of loop 
      if not b: 
       break 

      # Get the location of \" 
      s, e = b.span(1) 
      c = jsonStr[s:e] 

      # Replace \" with \' 
      c = c.replace('"',"'") 
      jsonStr = jsonStr[:s] + c + jsonStr[e:] 
     return json.loads(jsonStr) 

このコードはまた、問題文の


ORあなたも行うことができますに言及したJSON文字列のために働きますこれは:

def fixJSON(jsonStr): 
    # First remove the " from where it is supposed to be. 
    jsonStr = re.sub(r'\\', '', jsonStr) 
    jsonStr = re.sub(r'{"', '{`', jsonStr) 
    jsonStr = re.sub(r'"}', '`}', jsonStr) 
    jsonStr = re.sub(r'":"', '`:`', jsonStr) 
    jsonStr = re.sub(r'":', '`:', jsonStr) 
    jsonStr = re.sub(r'","', '`,`', jsonStr) 
    jsonStr = re.sub(r'",', '`,', jsonStr) 
    jsonStr = re.sub(r',"', ',`', jsonStr) 
    jsonStr = re.sub(r'\["', '\[`', jsonStr) 
    jsonStr = re.sub(r'"\]', '`\]', jsonStr) 

    # Remove all the unwanted " and replace with ' ' 
    jsonStr = re.sub(r'"',' ', jsonStr) 

    # Put back all the " where it supposed to be. 
    jsonStr = re.sub(r'\`','\"', jsonStr) 

    return json.loads(jsonStr) 
+0

素敵ですが、テキストからすべての '' 'を削除するのではなく、文字列の中に見つからなかったいくつかのプレースホルダcharを置き換えて、エスケープされた適切な '' 'の後に引用符を入れましたか? –

+0

これは適切な方法です。 – theBuzzyCoder

関連する問題