When I started learning Go, my first real task was to merge two JSON files and validate the result. It sounded easy. It turned out to be a great way to learn how Go handles file I/O, JSON, maps, and type safety.
Here’s how I did it, what I learned, and some alternatives I found along the way.
The Task
- Read two JSON files.
- Merge them into a single structure.
- Validate the result to make sure certain fields exist and are of the correct type.
- Write the final JSON to a new file.
Step 1: Reading the JSON Files
Go has great support for reading files and decoding JSON using only the standard library.
data1, _ := ioutil.ReadFile("file1.json")
data2, _ := ioutil.ReadFile("file2.json")
var json1 map[string]interface{}
var json2 map[string]interface{}
json.Unmarshal(data1, &json1)
json.Unmarshal(data2, &json2)
Note: In newer versions of Go, you can use os.ReadFile
instead of ioutil.ReadFile
, since ioutil
is deprecated.
Step 2: Merging the JSON Maps
I needed a function to merge two maps. If a key existed in both maps, the value from the second map should overwrite the first. If the value was a nested object, I needed to merge it recursively.
Here’s the function I wrote:
func mergeMaps(m1, m2 map[string]interface{}) map[string]interface{} {
for k, v2 := range m2 {
if v1, ok := m1[k]; ok {
if v1Map, ok := v1.(map[string]interface{}); ok {
if v2Map, ok := v2.(map[string]interface{}); ok {
m1[k] = mergeMaps(v1Map, v2Map)
continue
}
}
}
m1[k] = v2
}
return m1
}
This handled both flat and nested JSON structures correctly.
Step 3: Validating the JSON
Next, I wrote a simple validation function to check that certain fields were present and of the expected type. Since I wasn’t using a schema validator, I had to do manual checks.
func validateJSON(data map[string]interface{}) error {
if _, ok := data["name"].(string); !ok {
return fmt.Errorf("missing or invalid 'name' field")
}
if port, ok := data["port"]; !ok || reflect.TypeOf(port).Kind() != reflect.Float64 {
return fmt.Errorf("missing or invalid 'port' field")
}
return nil
}
Note: When decoding JSON into interface{}
, numbers are treated as float64
by default, which can be confusing at first.
Step 4: Writing the Result
Finally, I wrote the merged and validated JSON back to a file using pretty formatting.
resultBytes, _ := json.MarshalIndent(merged, "", " ")
ioutil.WriteFile("merged_output.json", resultBytes, 0644)
It worked exactly as expected.
Alternatives: Using a Package to Merge JSON
, I discovered some packages make JSON merging easier. These are a few good ones:
1. mergo
mergo
is a popular library for merging maps and structs in Go. It supports nested merges and lets you choose whether to overwrite values.
Install:
go get github.com/imdario/mergo
Example:
var a map[string]interface{}
var b map[string]interface{}
json.Unmarshal([]byte(`{"config": {"debug": false}}`), &a)
json.Unmarshal([]byte(`{"config": {"debug": true, "port": 8080}}`), &b)
mergo.Merge(&a, b, mergo.WithOverride)
Use this if you want clean, deep merging without writing recursive code.
2. json-patch
(go-patch)
json-patch
lets you apply JSON Patch operations. It’s more for diffing and applying structured updates, but still useful for working with JSON dynamically.
Install:
go get github.com/evanphx/json-patch
Use this if you’re working with patches or want to programmatically update JSON with control.
3. mapstructure
mapstructure
helps decode dynamic maps into strongly typed structs. It’s not a merge tool by itself but works well after you merge JSON data and want to bind it to a Go struct.
Install:
go get github.com/mitchellh/mapstructure
Use this when you want structure and type safety after parsing JSON.
Final Thoughts
This task helped me learn a lot about how Go handles data, how JSON works under the hood, and why strong typing matters. Writing a merge function gave me control, but using libraries like mergo
made the code more maintainable later.
If you’re learning Go, I recommend starting with small tasks like this. You’ll get real experience with files, encoding, maps, and error handling, all while solving a practical problem.
It’s a small project, but one that taught me a lot.