1

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?

2 Answers 2

3

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

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

2 Comments

This is the easiest solution for marshalling and the worst for unmarshalling - as long as this only needs to write to JSON and never read it, this is the solution I'd recommend.
@Adrian right, of course, I certainly didn't mean this to be a valid solution for unmarshaling, however, if one needed to, one could, without too much effort, extend the above approach to accommodate the unmarshaling of such a json, and I am sure you yourself are aware of that. I do wonder though what you think of the following approach: play.golang.org/p/u7Cnija2xC8 Is that an approach you yourself would also use, or would you approach it in a different way?
1

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

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.