用未知字段解组JSON

时间:2015-10-30 13:06:06

标签: json go

我有以下JSON

{"a":1, "b":2, "?":1, "??":1}

我知道它有" a"和" b"字段,但我不知道其他字段的名称。所以我想以下列类型解组它:

type Foo struct {
  // Known fields
  A int `json:"a"`
  B int `json:"b"`
  // Unknown fields
  X map[string]interface{} `json:???` // Rest of the fields should go here.
}

我该怎么做?

7 个答案:

答案 0 :(得分:19)

这不好,但你可以通过实施Unmarshaler

来实现
type _Foo Foo

func (f *Foo) UnmarshalJSON(bs []byte) (err error) {
    foo := _Foo{}

    if err = json.Unmarshal(bs, &foo); err == nil {
        *f = Foo(foo)
    }

    m := make(map[string]interface{})

    if err = json.Unmarshal(bs, &m); err == nil {
        delete(m, "a")
        delete(m, "b")
        f.X = m
    }

    return err
}

类型_Foo是解码时避免递归的必要条件。

答案 1 :(得分:19)

解散两次

一种选择是解组两次:一次输入Foo类型的值,一次输入map[string]interface{}类型的值,然后移除键"a""b":< / p>

type Foo struct {
    A int                    `json:"a"`
    B int                    `json:"b"`
    X map[string]interface{} `json:"-"` // Rest of the fields should go here.
}

func main() {
    s := `{"a":1, "b":2, "x":1, "y":1}`
    f := Foo{}
    if err := json.Unmarshal([]byte(s), &f); err != nil {
        panic(err)
    }

    if err := json.Unmarshal([]byte(s), &f.X); err != nil {
        panic(err)
    }
    delete(f.X, "a")
    delete(f.X, "b")

    fmt.Printf("%+v", f)
}

输出(在Go Playground上尝试):

{A:1 B:2 X:map[x:1 y:1]}

解组一次并手动处理

另一种选择是在map[string]interface{}解组一次并手动处理Foo.AFoo.B字段:

type Foo struct {
    A int                    `json:"a"`
    B int                    `json:"b"`
    X map[string]interface{} `json:"-"` // Rest of the fields should go here.
}

func main() {
    s := `{"a":1, "b":2, "x":1, "y":1}`
    f := Foo{}
    if err := json.Unmarshal([]byte(s), &f.X); err != nil {
        panic(err)
    }
    if n, ok := f.X["a"].(float64); ok {
        f.A = int(n)
    }
    if n, ok := f.X["b"].(float64); ok {
        f.B = int(n)
    }
    delete(f.X, "a")
    delete(f.X, "b")

    fmt.Printf("%+v", f)
}

输出相同(Go Playground):

{A:1 B:2 X:map[x:1 y:1]}

答案 2 :(得分:8)

最简单的方法是使用这样的界面:

var f interface{}
s := `{"a":1, "b":2, "x":1, "y":1}`

if err := json.Unmarshal([]byte(s), &f); err != nil {
    panic(err)
}

Go Playground example

答案 3 :(得分:7)

几乎单程,使用 <div id="slider"></div> <script> $(function() { var extensionMethods = { pips: function( settings ) { options = { first: "number", // "pip" , false last: "number", // "pip" , false rest: "pip" // "number" , false }; $.extend( options, settings ); // get rid of all pips that might already exist. this.element.addClass('ui-slider-pips').find( '.ui-slider-pip' ).remove(); // we need teh amount of pips to create. var pips = this.options.max - this.options.min; // for every stop in the slider, we create a pip. for( i=0; i<=pips; i++ ) { // hold a span element for the pip var s = $('<span class="ui-slider-pip"><span class="ui-slider-line"></span><span class="ui-slider-number">'+i+'</span></span>'); // add a class so css can handle the display // we'll hide numbers by default in CSS, and show them if set. // we'll also use CSS to hide the pip altogether. if( 0 == i ) { s.addClass('ui-slider-pip-first'); if( "number" == options.first ) { s.addClass('ui-slider-pip-number'); } if( false == options.first ) { s.addClass('ui-slider-pip-hide'); } } else if ( pips == i ) { s.addClass('ui-slider-pip-last'); if( "number" == options.last ) { s.addClass('ui-slider-pip-number'); } if( false == options.last ) { s.addClass('ui-slider-pip-hide'); } } else { if( "number" == options.rest ) { s.addClass('ui-slider-pip-number'); } if( false == options.rest ) { s.addClass('ui-slider-pip-hide'); } } // if it's a horizontal slider we'll set the left offset, // and the top if it's vertical. if( this.options.orientation == "horizontal" ) s.css({ left: '' + (100/pips)*i + '%' }); else s.css({ top: '' + (100/pips)*i + '%' }); // append the span to the slider. this.element.append( s ); } } }; $.extend(true, $['ui']['slider'].prototype, extensionMethods); $("#slider").slider({ min: 0, max: 600, step: 100, // on slide adjust width of all rects slide: function(event, ui) { svg.selectAll("rect") .attr("width", function (d) {

我们可以解组到json.RawMessage,然后分别解组每个字段。

JSON将被标记化两次,但这非常便宜。

可以使用以下辅助函数:

map[string]json.RawMessage

以下是Go Playground的完整代码 - http://play.golang.org/p/EtkJUzMmKt

答案 4 :(得分:1)

单次通过,使用github.com/ugorji/go/codec

当解组到map时,encoding/json清空地图,但ugorji/go/codec没有。它还尝试填充现有值,因此我们可以将指针foo.A,foo.B放入foo.X:

package main

import (
    "fmt"
    "github.com/ugorji/go/codec"
)

type Foo struct {
    A int
    B int
    X map[string]interface{}
}

func (this *Foo) UnmarshalJSON(jsonStr []byte) (err error) {
    this.X = make(map[string]interface{})
    this.X["a"] = &this.A
    this.X["b"] = &this.B
    return codec.NewDecoderBytes(jsonStr, &codec.JsonHandle{}).Decode(&this.X)
}

func main() {
    s := `{"a":1, "b":2, "x":3, "y":[]}`
    f := &Foo{}
    err := codec.NewDecoderBytes([]byte(s), &codec.JsonHandle{}).Decode(f)
    fmt.Printf("err = %v\n", err)
    fmt.Printf("%+v\n", f)
}

答案 5 :(得分:1)

使用Hashicorp的map-to-struct解码器,它可以跟踪未使用的字段:https://godoc.org/github.com/mitchellh/mapstructure#example-Decode--Metadata

它是双通的,但你不必在任何地方使用已知的字段名称。

func UnmarshalJson(input []byte, result interface{}) (map[string]interface{}, error) {
    // unmarshal json to a map
    foomap := make(map[string]interface{})
    json.Unmarshal(input, &foomap)

    // create a mapstructure decoder
    var md mapstructure.Metadata
    decoder, err := mapstructure.NewDecoder(
        &mapstructure.DecoderConfig{
            Metadata: &md,
            Result:   result,
        })
    if err != nil {
        return nil, err
    }

    // decode the unmarshalled map into the given struct
    if err := decoder.Decode(foomap); err != nil {
        return nil, err
    }

    // copy and return unused fields
    unused := map[string]interface{}{}
    for _, k := range md.Unused {
        unused[k] = foomap[k]
    }
    return unused, nil
}

type Foo struct {
    // Known fields
    A int
    B int
    // Unknown fields
    X map[string]interface{} // Rest of the fields should go here.
}

func main() {
    s := []byte(`{"a":1, "b":2, "?":3, "??":4}`)

    var foo Foo
    unused, err := UnmarshalJson(s, &foo)
    if err != nil {
        panic(err)
    }

    foo.X = unused
    fmt.Println(foo) // prints {1 2 map[?:3 ??:4]}
}

答案 6 :(得分:0)

我使用接口来解组不确定类型的json。

bytes := []byte(`{"name":"Liam","gender":1, "salary": 1}`)
var p2 interface{}
json.Unmarshal(bytes, &p2)
m := p2.(map[string]interface{})
fmt.Println(m)
相关问题