0

I need to marshal/unmarshal json to struct in golang. Assume the struct is

type A struct {
  Id string `json:"id"`
  Version string `json:"version"`
  Actors []actor `json:"actors`
  Payload struct {
     Name string `json:"name"`
     Number string `json:"number"`
  }
}
type payload struct {
    Name string `json:"name"`
    Number string `json:"number"`
}
type actor struct {
    Id   string `json:"id"`
    Type string `json:"type"`
    Role string `json:"role"`
}

The actors or payload maybe empty. The json maybe

{
  "id": "78a07cea-be2b-499c-b82b-e4f510260484",
  "version": "1.0.0",
  "actors": [
    {
      "id": "1234567",
      "type": "XXX",
      "role": "111"
    },
    {
      "id": "7654321",
      "type": "YYY",
      "role": "222"
    }
  ],
  "payload": ""
}

or

{
  "id": "78a07cea-be2b-499c-b82b-e4f510260484",
  "version": "1.0.0",
  "actors": [],
  "payload": {
       "name": "XXXX",
       "number": "1234567"
   }
}

If i follow the struct A design and try to marshal json with payload empty, i have to init as below

a := A{
  Id: "78a07cea-be2b-499c-b82b-e4f510260484",
  Version: "1.0.0",
  Actors: []actor{
    actor{
      Id: "1234567",
      Type: "XXX",
      Role: "111",
    },
    actor{
      Id: "7654321",
      Type: "YYY",
      Role: "222",
    },
  },
  Payload: payload{},
}

Which will result in below json with one empty payload struct

{
  "id": "78a07cea-be2b-499c-b82b-e4f510260484",
  "version": "1.0.0",
  "actors": [
    {
      "id": "1234567",
      "type": "XXX",
      "role": "111"
    },
    {
      "id": "7654321",
      "type": "YYY",
      "role": "222"
    }
  ],
  "payload": {
     "name":"",
     "number":""
   }
}

Is there any way i can generate

"payload": ""

instead of blank payload struct? Or is there any other struct design for this kind of json format? BTW i cannot pass nil to Payload struct.

0

2 Answers 2

4

The json.Marshaler interface can be implemented to customize JSON encoding, and the json.Unmarshaler interface for decoding (left as an exercise for the reader):

package main

import (
    "encoding/json"
    "fmt"
)

type A struct {
    Payload payload
}

type payload struct {
    Name   string `json:"name"`
    Number string `json:"number"`
}

func (p payload) MarshalJSON() ([]byte, error) {
    if p.Name == "" && p.Number == "" {
        return []byte(`""`), nil
    }

    type _payload payload // prevent recursion
    return json.Marshal(_payload(p))
}

func main() {
    var a A
    b, _ := json.MarshalIndent(a, "", "  ")
    fmt.Println(string(b))

    a.Payload.Name = "foo"
    b, _ = json.MarshalIndent(a, "", "  ")
    fmt.Println(string(b))
}

// Output:
// {
//   "Payload": ""
// }
// {
//   "Payload": {
//     "name": "foo",
//     "number": ""
//   }
// }

Try it on the playground: https://play.golang.org/p/9jhSWnKTnTf

The ad-hoc _payload type is required to prevent recursion. If one would write return json.Marshal(p), the json package would call MarshalJSON again, because p is of type payload, and payload implements json.Marshaler.

The _payload type has the same underlying type as payload but does not implement json.Marshaler (see Type definitions for details), so it is encoded using the standard rules of the json package; it produces exactly the same output that encoding a value of type payload would produce if payload didn't implement json.Marshaler.

Sign up to request clarification or add additional context in comments.

1 Comment

@Peter Can you explain type _payload payload // prevent recursion whats happening here or what should I read to learn about this ? :)
1

Check if using omitempty with json struct tag helps. I think it will result in "payload": {} instead of "payload": ""

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.