Home > Back-end >  GOLANG unmarshal dynamic JSON
GOLANG unmarshal dynamic JSON

Time:12-20

I am very new to GOLANG.

I have been trying for quite some time now to unmarshal an ethereum RPC JSON which has a dynamic structure. No GOLANG struct and map setup I did worked and I am able to get the stateDiff entries (3) but all lower structs seem not to be filled ith any data. So I am able to loop through all the 3 entries but then don't know how to access the values below and when dumping the unmarshal result, I see that GOLANG is not delivering the data anyway into StateDiff

JSON FILE:

{
   "jsonrpc":"2.0",
   "id":1,
   "result":{
      "output":"0x0000000000000000000000000000000000000000000000000000000000000001",
      "stateDiff":{
         "0x0000000000000000000000000000000000000000":{
            "balance":{
               "*":{
                  "from":"0x45acecdfadb71366cf",
                  "to":"0x45aced3909536ccacf"
               }
            },
            "code":"=",
            "nonce":"=",
            "storage":{
               
            }
         },
         "0x07865c6e87b9f70255377e024ace6630c1eaa37f":{
            "balance":"=",
            "code":"=",
            "nonce":"=",
            "storage":{
               "0x86a60af761556602732bbdeaef13ba6e2481f83362d3489389f51353d86a6ac3":{
                  "*":{
                     "from":"0x0000000000000000000000000000000000000000000000000000000000000000",
                     "to":"0x0000000000000000000000000000000000000000000000000000000000002710"
                  }
               },
               "0xb0cf6f3c0836765b9dee3d1537458f10fe99447508adc172c1f633ac7352aaa8":{
                  "*":{
                     "from":"0x00000000000000000000000000000000000000000000000000092f379a04d2b0",
                     "to":"0x00000000000000000000000000000000000000000000000000092f379a04aba0"
                  }
               }
            }
         },
         "0x6dbe810e3314546009bd6e1b29f9031211cda5d2":{
            "balance":{
               "*":{
                  "from":"0x41c41fc2c0247860",
                  "to":"0x41c3c66723c4155c"
               }
            },
            "code":"=",
            "nonce":{
               "*":{
                  "from":"0x741",
                  "to":"0x742"
               }
            },
            "storage":{
               
            }
         }
      },
      "trace":[
         
      ],
      "vmTrace":null
   }
}

I have tried to unmarshal the JSON into the following structure (among many) and i can't get the values such as result>stateDiff>0x0000000000000000000000000000000000000000>balance>*>from Struct below is just one of many i tried. I can't get anything below the entry 0x0000000000000000000000000000000000000000


type structChange struct {
    Changes map[string]string `json:"*"`
}

type structStateDiff struct {
    Balance *structChange            `json:"balance"`
    Code    string                    `json:"code"`
    Nonce   string                    `json:"nonce"`
    Storage map[string]*structChange `json:"storage"`
}

type res_trace_replayTransaction struct {
    Jsonrpc string `json:"jsonrpc"`
    ID      int    `json:"id"`
    Result  struct {
        Output    string                      `json:"output"`
        StateDiff map[string]*structStateDiff `json:"stateDiff"`
        Trace     []interface{}               `json:"trace"`
        VMTrace   interface{}                 `json:"vmTrace"`
    } `json:"result"`
}

EDIT: Code for umarshal

retObj := rpcCall(jstring)

var callResponse res_trace_replayTransaction
err := json.Unmarshal(retObj, &callResponse)

CodePudding user response:

Note that by default the encoding/json package can unmarshal a JSON string into a Go string, and it can unmarshal a JSON object into a Go map or a Go struct. Additionally it can unmarshal any JSON value into an empty interface{}.

Also note that Go is a statically typed language, if you specify a value to be of type T1 then, at runtime, you cannot change it's type to T2. There is just no way to do it, no way to change a value's type.

So if you define a field to be of some struct type, you cannot, by default, unmarshal a JSON string into it. And equally if you define a field to be of type string, you cannot, by default, unmarshal a JSON object into it.

But because JSON itself allows for a dynamic structure the encoding/json package provides two interfaces that give you the ability to customize how the JSON is marshaled and unmarshaled.

So if you have a JSON property (e.g. "balance" or "nonce") that can be either "=" (a string), or { ... } (an object), you will need to declare a custom type that implements the json.Marshaler and json.Unmarshaler interfaces that know how to properly marshal and unmarshal the JSON value.

For example:

type structChange struct {
    Changes map[string]string `json:"*"`
}

func (s structChange) MarshalJSON() ([]byte, error) {
    // if empty retrun `"="`
    if len(s.Changes) == 0 {
        return []byte(`"="`), nil
    }

    // otherwise marshal as is
    type T structChange
    return json.Marshal(T(s))
}

func (s *structChange) UnmarshalJSON(data []byte) error {
    // if `"="`, ignore
    if string(data) == `"="` {
        return nil
    }

    // otherwise assume it's a valid object
    type T structChange
    return json.Unmarshal(data, (*T)(s))
}

NOTE: the temporary type T above is used to avoid a stack overflow caused by an infinite recursive call to the MarshalJSON and UnmarshalJSON methods.

https://go.dev/play/p/yfsTrMozZ2Z

  • Related