3

I'm having an issue unmarshalling a JSON response into a struct. The problem I'm having is that the zip code can either return as a string or an integer. How do I write an unmarshal method to check if the zip is an int and force it to store it as a string?

Struct:

type CustomerAddress struct { Line1 string `json:"line1"` City string `json:"city"` State string `json:"state"` Zip string `json:"zip"` IsPrimaryAddress string `json:"isPrimaryAddress"` } 

Example Json:

address": [ { "line1": "555 ADDRESS PLACE", "city": "DALLAS", "state": "TX", "isPrimaryAddress": "Y", "zip": 55555 } ] 

After unmarshalling, the result should have the zip successfully converted into a string:

address": [ { "line1": "555 ADDRESS PLACE", "city": "DALLAS", "state": "TX", "isPrimaryAddress": "Y", "zip": "55555" } ] 

As an attempt, I tried to use a ZipWrapper.

type CustomerAddress struct { Line1 string `json:"line1"` City string `json:"city"` State string `json:"state"` Zip ZipWrapper `json:"zip"` IsPrimaryAddress string `json:"isPrimaryAddress"` } type ZipWrapper struct { Zip string } func (w *ZipWrapper ) UnmarshalJSON(data []byte) (err error) { if zip, err := strconv.Atoi(string(data)); err == nil { w.Zip = strconv.Itoa(zip) return nil } return json.Unmarshal(data, &w.Zip) } 

This almost worked except the zip is now a nested struct within CustomerAddress which is not what I want:

 address": [ { "line1": "555 ADDRESS PLACE", "city": "DALLAS", "state": "TX", "isPrimaryAddress": "Y", "zip": { "Zip": "55555" } } ] 

Any ideas? I feel like this is a relatively easy task but I'm a complete Go noob and haven't fully wrapped my head around how Unmarshalling works.

2 Answers 2

13

The json package provides the json.Number type to do this:

type CustomerAddress struct { Line1 string `json:"line1"` City string `json:"city"` State string `json:"state"` Zip json.Number `json:"zip"` IsPrimaryAddress string `json:"isPrimaryAddress"` } 

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

If you needed to do this yourself without the nested struct, you can declare the type the same way as json.Number, with string as the underlying type

type ZipWrapper string func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) { if len(data) > 1 && data[0] == '"' && data[len(data)-1] == '"' { data = data[1 : len(data)-1] } if _, err := strconv.Atoi(string(data)); err != nil { return err } *w = ZipWrapper(string(data)) return nil } 
Sign up to request clarification or add additional context in comments.

3 Comments

Just tried using json.Number and the zip is returning as a number instead of a string in the final struct. It absolutely has to be a string because client applications that consume this are expecting it as a string. Can you expound a bit about defining a ZipWrapper type as a string? Thanks for the quick response!
@xPeaWhyTee: what do you mean by "the zip is returning as a number"? json.Number is a string, though it has methods to convert it to an int64 or float64.
The zip was returning as: {"zip": 55555} instead of {"zip": "55555"} when using json.Number as a field type. But I realized that I read the second part of your answer too quickly and misinterpreted what you were saying. Your Unmarshal function works perfectly. Thanks!
2

What Jim is saying in the other answer about defining ZipWrapper as a string is that you can take the same approach you were taking, but without the nested Struct.

Like, define the field like this:

Zip ZipWrapper `json:"zip"` 

But then ZipWrapper is defined like:

type ZipWrapper string 

Your UnmarshalJSON function can be like this:

func (w *ZipWrapper) UnmarshalJSON(data []byte) (err error) { if zip, err := strconv.Atoi(string(data)); err == nil { str := strconv.Itoa(zip) *w = ZipWrapper(str) return nil } var str string err = json.Unmarshal(data, &str) if err != nil { return err } return json.Unmarshal([]byte(str), w) } 

Here's a working Go playground:

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

1 Comment

Thanks for the clarification. I actually realized this after rereading his answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.