5

I'm trying to retrieve data from an API call and pass them to another service. The data i received are in a specific JSON structure and i would like to map it to a struct, but without the multiple levels of data. I tried the dot notation to access deeper value, but it doesn't work.

Basically, i'm trying to get a struct with an array of "issues" (key, self, description), but without having the "fields.description" structure.

The JSON:

{
  "ticket": {
    "number": "2",
    "issues": [
      {
        "key": "TA-2",
        "self": "http://localhost:8080/rest/api/2/issue/10100",
        "fields": {
          "description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|personal_project|Enter the name of your openshift project|\n|memory|8GB|Enter the desired amount of memory|"
        }
      },
      {
        "key": "TA-1",
        "self": "http://localhost:8080/rest/api/2/issue/10000",
        "fields": {
          "description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|my_openshift_project|Enter the name of your openshift project|\n|memory|4GB|Enter the desired amount of memory|"
        }
      }
    ]
  }
}

The Struct:

type Todo struct {
  Number string `json:"number"`
  Issue  []struct {
      Key         string `json:"key"`
      Self        string `json:"self"`
      Description string `json:"field.description"` //doesn't work. don't know what to put ...
  } `json:"issues"`
}

The expected / desired struct:

{
  "number": "2",
  "issues": [{
    "key": "TA-2",
    "self": "http://localhost:8080/rest/api/2/issue/10100",
    "description": "This template ..."
  }, {
    "key": "TA-1",
    "self": "http://localhost:8080/rest/api/2/issue/10000",
    "description": "This template ..."
  }]
}

Is it possible? if yes, how to do it? Using nested struct won't change the initial JSON structure.

Thanks

2 Answers 2

2

You have at least three options:

  1. Create a separate set of struct types to represent the data in the format provided by the JSON. This might include a TodoList struct with a ticket field, referring to a separate Todo struct. This struct might have a number field and an issues field. Each Issue struct might have a fields field, which in turn has a description field. Basically, you recreate the entire domain as represented in the JSON, then you can simply call json.Unmarshal() to unmarshal the JSON into the TodoList. Once you have a TodoList, simply convert it to the representation that you desire by hand.
  2. Write your own custom UnmarshalJSON() function for the Todo and Issue structs. Click here for an example from the GoLang docs.
  3. Unmarshal the entire JSON document manually in an external function. This might involve unmarshalling the data to a map[string]json.RawMessage, and then mapping many of those json.RawMessage objects to other maps and objects, and so on.

The first option is better for cases where you simply have an alternate representation of the data which you desire, but all of the data is still in the JSON document, and all of the data in the JSON document is still relevant. This seems to match your predicament, and so this is the option I would recommend.

The second option is better for cases where you would simply like to recognize that the JSON encoding for a structure should provide a consistently different representation from the GoLang object encoding. In other words, when the structure is encoded as a GoLang object, it should take one form, whereas when the structure is encoded in JSON, it should take another form. These two forms must be strictly tied to their encodings (JSON vs GoLang object). If you plan on unmarshaling the JSON to a GoLang object, and then marshaling it back into a different JSON representation from what you had to begin with, this is probably not the best option because json.Unmarshal() and json.Marshal() should be inverses of each other. Otherwise, you have violated the principle of least astonishment. This option can also be used for delaying or preventing unmarshaling of nested properties by way of json.RawMessage objects. Thus, if you have some dynamically typed JSON field whose type is determined upon examination of other fields, or if you have large amounts of irrelevant data in the JSON document, you can delay or prevent the unmarshalling of such data until desired.

In practice, the third option is very similar to the second. However, by providing an external unmarshaling mechanism rather than implementing UnmarshalJSON(), you are less likely to break the principle of least astonishment. That is, the perceived contract between json.Unmarshal() and json.Marshal() is necessarily preserved. Thus, you can marshal and unmarshal data however you would like, and you can maintain as many different representations per encoding as you would like. That being said, the first option provides this very same benefit, and it is much easier. In reality, the third option should only ever be chosen if the first and second options will not suffice, such as when you may need to delay or prevent unmarshaling of nested properties (like in the second option) while simultaneously providing multiple different JSON representations and preserving the contract between json.Marshal() and json.Unmarshal() (like in the first option).

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

Comments

0

You cannot add a dot notation in json struct tags. If you expect multiple fields under fields and if you are sure that each field value is a string, then you can unmarshal your JSON into

type Todo struct {
  Number string `json:"number"`
  Issue  []struct {
      Key         string `json:"key"`
      Self        string `json:"self"`
      Fields map[string]string `json:"fields"`
  } `json:"issues"`
}

If its only description everytime, then you can add

Fields struct {
    Description string `json:"description"`
} `json:"fields"`

1 Comment

Thanks for the reply, but it doesn't solve my issue, as it will produce a struct with the same representation as the JSON; i want to "simplify" the JSON structure while matching it with my struct. I added the "expected / desired" struct in the OP.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.