0

I have a struct containing 4 fields:

type Animal struct {
    Name string
    Age  int
    Zone int
}

I am doing a post request sending a json object to decode as the struct, the json should look like this:

{
"Age":10,
"Name":"Lion", 
"Zone":1,
}

I want all fields to be field, but when ever I don't fill all the fields and send some json like.

{
"Age":10,
"Zone":1,
}

The json.Decoder automatically build that Filed and set it as "" (which is zero value for the type) instead of null.

How can I set null value or check it for being null and generate an error ?

I expect the result to be {Age:10, Zone:1, Name:null} or at least generate an error!

this is the code I use to convert json to struct

animalModel := Animal{}
err := json.NewDecoder(r.Body).Decode(&animalModel)
Flimzy
  • 68,325
  • 15
  • 126
  • 165
Mahmood Bkh
  • 409
  • 2
  • 4
  • 14
  • 3
    See https://stackoverflow.com/questions/31048557/assigning-null-to-json-fields-instead-of-empty-strings – icio Dec 29 '19 at 16:14
  • 1
    Does this answer your question? [Assigning null to JSON fields instead of empty strings](https://stackoverflow.com/questions/31048557/assigning-null-to-json-fields-instead-of-empty-strings) – icio Dec 29 '19 at 16:14

2 Answers2

1

You can use a pointer

type Animal struct {
    Name *string
    Age  int
    Zone int
}

or a package

import "github.com/guregu/null"

type Animal struct {
    Name null.String
    Age  int
    Zone int
}
KevBurnsJr
  • 4,834
  • 2
  • 24
  • 16
  • when i use pointer and send a json with no name field i recive a panic saying :runtime error: invalid memory address or nil pointer dereference – Mahmood Bkh Dec 29 '19 at 22:30
1

string can't be nil. If you want to distinguish between absent field, null value and empty value "", you've got these options:

  1. As a quick solution you can use a pointer:
type Animal struct {
Name *string
}

The drawback is that you need to use a temporal variable to set this field.

  1. A more flexible solution is to implement Unmarshaler interface for Animal struct and use temporal map[string]string to make desired checks and return an error:
type Animal struct {
Name string
}

func (a *Animal) UnmarshalJSON(data []byte) error {
  m := map[string]interface{}{}
  err := json.Unmarshal(data, &m)
  if err != nil {
    return err
  }
  // check if field is exist
  name, exist := m["name"]
  if !exist {
    return fmt.Errorf("field 'name' should be present")
  }
  // check other fields...
  // ...

  // if all fields are ok, you can:
  // 1. set fields from map items
  a.Name = name
  ...

  // 2. call Unmarshal
  return json.Unmarshal(data, a)
}

The drawback here is that you need to check each field "by hands".

  1. The next step is to use heavy machinery: go-swagger or go-openapi. These instruments give you the ability to use declarative schema (OpenAPI) to perform checks over JSON data, you can easily implement versioning, publish your APIs, and more. Of course, it is overengineering for a 3 field struct.
diafour
  • 51
  • 3