From json_encode to json.Marshal: A PHP Developer’s Journey into Go’s JSON World

From json_encode to json.Marshal: A PHP Developer’s Journey into Go’s JSON World
Photo by alessandro fazari / Unsplash

As a PHP developer, working with JSON feels like second nature. You build arrays or objects, run json_encode(), and send it off. Done. Need to decode JSON? json_decode() and you're ready to manipulate data however you like. Dynamic, fast, and no fuss.

But then comes Go, with its strong typing, structure-first approach, and something called "marshalling". Suddenly, JSON isn't as plug-and-play anymore.

Let me walk you through what I learned — and what I wish I knew earlier — when transitioning from PHP’s JSON handling to Go’s.


1. PHP: JSON the Free-Spirited Way

In PHP, you can build JSON from scratch without a care for types or structure:

$data = [
    "name" => "Sony",
    "age" => 30,
    "skills" => ["PHP", "Go", "JavaScript"]
];

echo json_encode($data);
// Output: {"name":"Sony","age":30,"skills":["PHP","Go","JavaScript"]}

Want to add more fields dynamically?

$data["location"] = ["city" => "Jakarta"];

No need to define the shape of the data beforehand. This flexibility is powerful — especially for quick prototypes, APIs, or dynamic systems.


2. Go: JSON the Disciplined Way

In Go, JSON is a bit like checking into a secure building: you need your ID, clearance level, and a defined purpose.

Here’s how you define a structure before working with JSON:

type Person struct {
	Name   string   `json:"name"`
	Age    int      `json:"age"`
	Skills []string `json:"skills"`
}

And then marshal (encode) it:

person := Person{
	Name:   "Sony",
	Age:    30,
	Skills: []string{"PHP", "Go", "JavaScript"},
}

jsonData, _ := json.Marshal(person)
fmt.Println(string(jsonData))

Result:

{"name":"Sony","age":30,"skills":["PHP","Go","JavaScript"]}

It’s verbose compared to PHP, but you get compile-time safety, clear structure, and reliable data contracts.


3. What is “Marshalling” Anyway?

The terms are intimidating at first, but they’re quite simple:

  • Marshalling: Convert Go data (struct, map, etc.) → JSON
    • Similar to json_encode() in PHP
  • Unmarshalling: Convert JSON → Go data (typically struct or map)
    • Similar to json_decode() in PHP

In Go:

jsonStr := `{"name":"Sony","age":30}`
var person Person
json.Unmarshal([]byte(jsonStr), &person)

You must provide a pointer (&person) to store the result.


4. Can Go Handle Dynamic JSON Like PHP?

Yes — but with a twist. You need to use map[string]interface{}, Go's version of a dynamic JSON object.

data := map[string]interface{}{
	"name": "Sony",
	"age":  30,
	"skills": []string{
		"PHP", "Go", "JS",
	},
}
jsonData, _ := json.Marshal(data)

And if you're decoding JSON where the structure is unknown:

var result map[string]interface{}
json.Unmarshal([]byte(`{"name":"Sony","age":30}`), &result)

fmt.Println(result["name"]) // Sony

But watch out: Go will infer types automatically, and you might need type assertions like:

age := result["age"].(float64) // Go treats JSON numbers as float64 by default

This is where Go feels a bit clunky compared to PHP.


5. Considerations You Might Miss at First

Here are things that took me a while to realize:

Struct Tags Matter

To match JSON field names with Go struct fields, use struct tags:

Name string `json:"name"`

Without this, Go expects JSON to use Name, not name.

omitempty Tag

To skip empty fields in the output:

Email string `json:"email,omitempty"`

If Email is empty, it won't be included in the JSON.

Custom Marshal/Unmarshal

You can implement MarshalJSON() and UnmarshalJSON() for custom behavior (like formatting time or handling nested JSON logic).

Performance & Safety

Go’s strict typing reduces runtime surprises. JSON bugs in PHP might go unnoticed until production. In Go, the compiler catches most of it.


6. So Which Is Better?

That depends on what you’re building:

Scenario Use PHP Use Go
Quick prototyping
Dynamic schema ⚠️ with map/interface
Production API ⚠️
Strict contracts
Type safety & speed

Finally

PHP makes working with JSON feel like jazz — improvised and freeform. Go is more like classical music — structured, precise, and intentional.

Once you get used to defining structs, handling types explicitly, and understanding what marshalling really means, you'll start to appreciate the clarity and safety Go offers — especially when building large, maintainable systems.

Dynamic is convenient. Structured is powerful.
In Go, the trade-off is worth it — especially in the long run.

Support Us