'Golang Unmarshal an JSON response, then marshal with Struct field names

So I am hitting an API that returns a JSON response and I am unmarshalling it into a struct like so:

package main



type ProcessedRecords struct {
    SLMIndividualID                  string      `json:"individual_id"`
    HouseholdPosition                int         `json:"Household Position"`
    IndividualFirstName              string      `json:"individual_first_name"`
}

func main() {
    req, _ := http.NewRequest(method, url, payload)

    res, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
    }
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Println(err)
    }
    
    fmt.Println(body)
    
    var responseObject Response
    json.Unmarshal(body, &responseObject)
    fmt.Println(responseObject)

which works great. However I need to marshal this struct again but I want to use the Struct Fields as keys instead of the json: ... fields. I am using the following code:

    recordsInput := []*firehose.Record{}
    for i := 0; i < len(records); i++ {
        if len(recordsInput) == 500 {
            * code to submit records, this part works fine *
        }

        b, err := json.Marshal(records[i])

        if err != nil {
            log.Printf("Error: %v", err)
        }

        record := &firehose.Record{Data: b}
        recordsInput = append(recordsInput, record)
    }

This does submit records successfully but it's in the format:

{"individual_id":"33c05b49-149b-480f-b1c2-3a3b30e0cb6f","Household Position":1...}

and I'd like it in the format:

{"SLMIndividualId":"33c05b49-149b-480f-b1c2-3a3b30e0cb6f","HouseholdPosition":1...}

How can I achieve this?

go


Solution 1:[1]

Those tags say how the struct should be marshalled, so if they are present, that is how the output will be. You'll need to convert it to a matching struct that does not have the json: tags:

type ProcessedRecords struct {
    SLMIndividualID     string `json:"individual_id"`
    HouseholdPosition   int    `json:"Household Position"`
    IndividualFirstName string `json:"individual_first_name"`
}

type ProcessedRecordsOut struct {
    SLMIndividualID     string
    HouseholdPosition   int
    IndividualFirstName string
}

func process() {
    var in ProcessedRecords
    json.Unmarshal(data, &in)
    // Convert to same type w/o tags
    out := ProcessedRecordsOut(in)
    payload, _ := json.Marshal(out)
    // ...
}

See a working example here: https://play.golang.org/p/p0Fc8DJotYE

Solution 2:[2]

You can omit fields one-way by defining a custom type and implementing the correct interface, e.g.

package main

import (
    "encoding/json"
    "fmt"
)

type Animal struct {
    Name  ReadOnlyString
    Order string
}

type ReadOnlyString string

func (ReadOnlyString) UnmarshalJSON([]byte) error { return nil }

func main() {
    x := Animal{"Bob", "First"}
    js, err := json.Marshal(&x)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Printf("%s\n", js)

    var jsonBlob = []byte(`{"Name": "Platypus", "Order": "Monotremata"}`)
    if err := json.Unmarshal(jsonBlob, &x); err != nil {
        fmt.Println("error:", err)
    }
    fmt.Printf("%#v\n\n", x)
}

https://go.dev/play/p/-mwBL0kIqM

Found this answer here: https://github.com/golang/go/issues/19423#issuecomment-284607677

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Adrian
Solution 2 user19007417