在不涉及所有字段的情况下封送和封送JSON正文

I'm looking for a way to unmarshal a JSON body without having to specify targets for all fields. And then be a able to "remarshal" the body with implicit fields untouched.

Something like this would be good, but doesn't work as a expected: (https://play.golang.org/p/fnVOKrmiFj)

package main

import (
    "encoding/json"
    "fmt"
)

type Transaction struct {
    Field1  string                  `json:"field1"`
    X       map[string]interface{}  `json:"-"`
}

func main() {
    body := []byte(`{"field1": "value1", "field2": "value2"}`)
    fmt.Printf("%+v
", string(body))

    var unmarshalledTransaction Transaction
    json.Unmarshal(body, &unmarshalledTransaction)
    fmt.Printf("%+v
", unmarshalledTransaction)

    remarshalledTransaction, _ := json.Marshal(&unmarshalledTransaction)
    fmt.Printf("%+v
", string(remarshalledTransaction))    
}

Gives the output

{"field1": "value1", "field2": "value2"}
{Field1:value1 X:map[]}
{"field1":"value1"}

My expected result would be that unmarshalledTransaction contains the "leftover" fields in the X fields. And they are then restored when Marshalling again.

Can this be done?

You would need to implement the MarshalJSON and UnmarshalJSON interfaces, and write your own logic to remap the fields to the appropriate spots:

func (t *Transaction) MarshalJSON() ([]byte, error) {
    data := t.X
    data["field1"] = t.Field1
    return json.Marshal(data)
}

func (t *Transaction) UnmarshalJSON(data []byte) error {
    m := make(map[string]interface{})
    json.Unmarshal(data, &m)
    t.Field1 = m["field1"].(string)
    delete(m, "field1")
    t.X = m
    return nil
}

https://play.golang.org/p/KBGAsXB0xA

If you want a generic solution (that would work with any struct without knowing the fields in advance), you can implement a function that would un-marshal the body into a struct and also return the "leftover" fields.

For that you'd also need to implement a function that would convert any given struct to a map (to be used to then manipulate maps in a generic way instead of known-in-advance structs).

Like so:

func structToMap(object interface{}) (map[string]interface{}, error) {
    tempJson, err := json.Marshal(object)
    if err != nil {
       return nil, err
    }
    var theMap map[string]interface{}
    err = json.Unmarshal(tempJson, &theMap)
    if err != nil {
        return nil, err
    }
    return theMap, nil
}

And then:

func unmarshalWithLeftovers(jsonBody []byte, target interface{}) (map[string]interface{}, error) {
    err := json.Unmarshal(jsonBody, target)
    if err != nil {
        return nil, err
    }
    structMap, err := structToMap(target)
    if err != nil {
        return nil, err
    }
    var leftOvers map[string]interface{}
    err = json.Unmarshal(jsonBody, &leftOvers)
    if err != nil {
        return nil, err
    }
    for k, _ := range structMap {
        delete(leftOvers, k)
    }
    return leftOvers, nil
}

You can then combine the struct and the leftovers map in a similar fashion to re-marshal everything.

See here a working example with the same type and json string that you used in your question:

https://play.golang.org/p/Fot6YVurHH