2017-12-05 8 views
0

私は、任意のjsonを解析し、85文字より長い文字列のキーまたは値をエンコード/書き換え(短いURLサービスの一種と考える)し、元のjsonにマーシャリングする必要があります。例: この{"Name": "Ed", "Text": "long characters....85 chars"}のような文書は、{"Name": "Ed", "Text": "short://ID=3403"}のようになります。この例は単純ですが、ネストされたオブジェクトや配列、基本的に未知の構造を持つ複雑な構造に対処する必要があります。不明なjson構造の文字列を書き換えることはできますか?

私の質問は、これは既知のライブラリや標準のエンコード/ jsonパッケージを使っても可能ですか? 私が実際に必要なのはtype stringMarshalerインタフェースの実装と同等であるが、我々はそれはので、私は

+0

「go」にする必要がありますか? – yeya

+0

はい。言語はGoです – user7425610

答えて

2

はい(標準エンコーディング/ JSONパッケージをフォークの横に)私が持っている他のオプションは、それが何であるかを思ったんだけどことはできません知っています可能であれば、標準ライブラリと少しのコードを使用します。

非整列化インターフェイスに{}アンマーシャル値トラバース

var v interface{} 
if err := json.Unmarshal(data, &v); err != nil { 
    // handle error 
} 

、あなたが行くように、文字列を短縮し、書き換え:

func shorten(v interface{}) interface{} { 
    switch v := v.(type) { 
    case []interface{}: 
     for i, e := range v { 
      v[i] = shorten(e) 
     } 
     return v 
    case map[string]interface{}: 
     m := make(map[string]interface{}) 
     for k, e := range v { 
      m[shortenString(k)] = shorten(e) 
     } 
     return m 
    case string: 
     return shortenString(v) 
    default: 
     return v 
    } 
} 

short://ID=xxxに長い文字列を変換するshortenString(s string) stringを呼び出しshorten機能。

元帥戻っJSONに値:

p, err := json.Marshal(shorten(v)) 
if err != nil { 
    // handle error 
} 

// p is a []byte 
fmt.Printf("%s\n", p) 

playground example

1

要件は、JSONに、あなたはJSONを取ることができる場合、新しい短縮バージョンで85文字を超えるすべての文字列を置換する場合有効なJSONです(つまり、入力が無効な場合に診断を行う必要がない場合)。ただ1つのregexp置換を使用できます。

有効なJSONでの引用符で囲まれた文字列は、したがって、あなただけの可能性の短縮バージョンでこのパターンのすべてのインスタンスを置き換えることができ

/"([^\\"]|\\.)*"/ 

と一致させることができます。

0

json.Decoderの方法を使用し、データ構造全体をメモリにデシリアライズしないようにする方法(テスト済み)です。 JSONドキュメントが非常に大きく、メモリオーバーヘッドを避けたいのであれば、ほとんどの場合、楽しく、そのメソッドをどのように使用できるかを説明するために行われます。

// For example 
r, w := os.Stdin, os.Stdout 

dec := json.NewDecoder(r) 
// Avoids any round-trip loss by leaving numbers un-parsed 
dec.UseNumber() 

// Current state (in an object or in an array; following the open 
// delim, or a key, or a value). The decoder has its own stack pretty 
// like this one, but it's private, so keep our own. 
const (
    jsonArrayStart = iota 
    jsonArrayVal 
    jsonObjectStart 
    jsonObjectKey 
    jsonObjectVal 
) 
stack := []byte{} 

for { 
    t, err := dec.Token() 
    if err == io.EOF { 
     break 
    } else if err != nil { 
     log.Fatal(err) 
    } 

    switch val := t.(type) { 
    case json.Delim: 
     // Copy delimiters out, and push/pop the state stack as appropriate 
     w.WriteString(string([]rune{rune(val)})) 
     switch val { 
     case '[': 
      stack = append(stack, jsonArrayStart) 
     case '{': 
      stack = append(stack, jsonObjectStart) 
     case ']', '}': 
      stack = stack[:len(stack)-1] 
     } 
     // The rest of the cases just copy values out 
    case nil: 
     w.WriteString("null") 
    case json.Number: 
     w.WriteString(string(val)) 
    case bool: 
     if val { 
      w.WriteString("true") 
     } else { 
      w.WriteString("false") 
     } 
    case string: 
     // Modify strings if called for (shortenString needs to be provided) 
     if len(val) >= 85 { 
      val = shortenString(val) 
     } 
     encoded, err := json.Marshal(val) 
     if err != nil { 
      log.Fatal(err) 
     } 
     w.Write(encoded) 
    } 

    if dec.More() { 
     // If there's more in the current array/object, write a colon or comma 
     // (if we just wrote a key/value), and set the next state. 
     // Arrays start with a value, and follow with more values. 
     // Objects start with a key and alternate between key and value. 
     switch stack[len(stack)-1] { 
     case jsonArrayStart: 
      stack[len(stack)-1] = jsonArrayVal 
     case jsonArrayVal: 
      w.WriteString(",") 
      // State remains jsonArrayVal 
     case jsonObjectStart: 
      stack[len(stack)-1] = jsonObjectKey 
     case jsonObjectKey: 
      w.WriteString(":") 
      stack[len(stack)-1] = jsonObjectVal 
     case jsonObjectVal: 
      w.WriteString(",") 
      stack[len(stack)-1] = jsonObjectKey 
     } 
    } else { 
     if len(stack) == 0 { 
      // End after the first complete value (array/object) in the stream 
      break 
     } 
     if stack[len(stack)-1] == jsonObjectKey { 
      // Should never happen 
      log.Fatal("Object key without a value?") 
     } 
    } 
} 
関連する問題