Dinesh Gowda
Dinesh Gowda

Reputation: 1154

Dynamically Encode json keys in go

I want a json output to look like whats shown below.Notice that based on the parameter type like document or text, the next key changes to document or text.

  "components": [
        {
          "type" : "header",
          "parameters": [
          {
            "type": "document",
            "document": {
            "filename":"dummy.pdf",
              "link": "https://en.unesco.org/inclusivepolicylab/sites/default/files/dummy-pdf_2.pdf"
            }
          }
        ]
        },
        {
          "type" : "body",
          "parameters": [
            {
              "type": "text",
              "text": "replacement_text"
            }
          ] 
         }
         ]

This are my struct definitions:

type Component struct {
    Type       string      `json:"type,omitempty"`
    Parameters []Parameter `json:"parameters,omitempty"`
}

type Parameter struct {
    Type            string `json:"type,omitempty"`
    TypeInformation map[string]interface{}
}

When i encode it it comes like this:

"components": [
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "TypeInformation": {
                "text": "Param1"
              }
            },
            {
              "type": "text",
              "TypeInformation": {
                "text": "param2"
              }
            }
          ]
        },
        {
          "type": "header",
          "parameters": [
            {
              "type": "document",
              "TypeInformation": {
                "document": {
                  "link": "http://link",
                  "filename": "dummy.pdf"
                }
              }
            }
          ]
        }
      ]

I dont want the TypeInformation key to come in the json, I just want the inner object. How can I do this?

Upvotes: 1

Views: 677

Answers (2)

mkopriva
mkopriva

Reputation: 38333

Rather than using a "generic" structure with an arbitrary map like you do with Parameter you could use different concrete types for each parameter type. Then, just drop them into a slice of empty interfaces and json.Marshal will know what to do.

type Object struct {
    Components []Component `json:"components"`
}

type Component struct {
    Type       string        `json:"type,omitempty"`
    Parameters []interface{} `json:"parameters,omitempty"`
}

type TextParameter struct {
    Type textType `json:"type"`
    Text string   `json:"text"`
}

type DocumentParameter struct {
    Type     documentType `json:"type"`
    Document Document     `json:"document"`
}

type Document struct {
    FileName string `json:"filename"`
    Link     string `json:"link"`
}

// used to "hard code" the type of the parameter
type textType struct{}

func (textType) MarshalJSON() ([]byte, error) { return []byte(`"text"`), nil }

// used to "hard code" the type of the parameter
type documentType struct{}

func (documentType) MarshalJSON() ([]byte, error) { return []byte(`"document"`), nil }

Then you can intialize an instance like so:

obj := Object{
    Components: []Component{{
        Type: "header",
        Parameters: []interface{}{
            DocumentParameter{Document: Document{
                FileName: "dummy.pdf",
                Link:     "https://en.unesco.org/inclusivepolicylab/sites/default/files/dummy-pdf_2.pdf",
            }},
        },
    }, {
        Type: "body",
        Parameters: []interface{}{
            TextParameter{Text: "replacement_text"},
        },
    }},
}

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

Upvotes: 3

praveent
praveent

Reputation: 610

You can customize the marshalling behavior of the struct if default golang behavior is not suitable. This is achieved by implementing Marshaler interface on the structure.

Example:

func (p Parameter) MarshalJSON() ([]byte, error) {
    r := fmt.Sprintf(`{"type":"%v",`, p.Type)
    switch p.Type {
        case "document":
            f := `"document":`
            b, _ := json.Marshal(p.TypeInformation[p.Type])
            r = r + f + string(b) + "}"
        case "text":
            f := `"text":`
            b, _ := json.Marshal(p.TypeInformation[p.Type])
            r = r + f + string(b) + "}"
    }
    return json.Marshal(r)
}

Working example here

Upvotes: 1

Related Questions