04 / 04

Explain how encoding/json marshaling works with struct tags and custom marshalers.

Difficulty: 5/10
struct tags, custom MarshalJSON, omitempty behavior

encoding/json uses struct tags to control JSON field names and behavior. Implement json.Marshaler and json.Unmarshaler interfaces for custom serialization logic.

Struct tags and custom marshalers
Common pitfalls
  1. 1

    Unexported fields are silently ignored — always export fields you want serialized

  2. 2

    interface{} numbers unmarshal as float64 by default — use UseNumber() for large integers

  3. 3

    omitempty omits zero values: 0, false, "", nil — careful with intentional zero values

  4. 4

    Anonymous embedded structs promote their fields to the parent — useful but can cause field collisions

  5. 5

    json.RawMessage allows deferring or passing through raw JSON without parsing

Scenario Questions

0-2 years experience

  1. 1You need to serialize a struct to JSON but want the field `UserID` to appear as `user_id` in the output. How would you write the struct tag?
  2. 2If you add `omitempty` to a struct tag, what JSON will be produced when the field has its zero value?
  3. 3You have a type that implements `MarshalJSON`. What will json.Marshal do when encoding a value of that type?

2-5 years experience

  1. 1We added a custom `MarshalJSON` to a struct to format a timestamp, but after a recent change the JSON output is missing some fields. Walk me through how you'd debug this.
  2. 2When integrating with an external API, you need to omit empty slices but include empty maps. How would you configure struct tags or custom marshalers to achieve that?
  3. 3Explain the trade‑offs between using struct tags with `omitempty` versus writing a custom `MarshalJSON` for a large struct with many optional fields.

5-8 years experience

  1. 1Our service processes millions of JSON messages per second. Discuss the performance implications of using custom `MarshalJSON` implementations versus relying on the default encoder with tags.
  2. 2We need to evolve a public API's JSON schema without breaking existing clients. How would you use struct tags and custom marshalers to deprecate a field while still supporting older versions?
  3. 3Describe how you would design a reusable library that provides common marshal/unmarshal behavior (e.g., snake_case conversion, time formatting) across many services, considering maintainability and testing.

8+ years experience

  1. 1Our organization is migrating from a legacy JSON library to Go's `encoding/json`. What architectural considerations would you address regarding struct tags, custom marshalers, and backward compatibility across multiple microservices?
  2. 2How would you set up a cross‑team guideline for handling JSON field naming conventions and custom marshaling to ensure consistency and avoid subtle bugs at scale?
  3. 3If you had to support both JSON and protobuf representations for the same data structures, how would you reconcile struct tags and custom marshalers to minimize duplication?

Follow-up Questions

  • What happens if a field has both a json tag and a MarshalJSON implementation?
  • How does omitempty decide whether a value is zero for different types?
  • Can you control the order of fields in the generated JSON?
Share

Share via WhatsApp, X, Facebook, LinkedIn or copy link. Open Graph preview enabled.