How to Flatten Nested JSON in Java, C#, Go, Rust, and Kotlin — Code Patterns + Browser Shortcut
- Every statically-typed language has its own JSON library — Jackson (Java), Newtonsoft (C#), encoding/json (Go), serde_json (Rust).
- None have a one-flag flatten; you write a small recursive helper.
- For one-off flattening, the browser tool is faster than writing and running the helper.
Table of Contents
Flattening nested JSON in Python takes one line (pd.json_normalize). In Java, C#, Go, Rust, or Kotlin, it is 15-30 lines of recursive traversal. None of these languages have a built-in JSON flatten helper. You either write one yourself, pull in a library, or — for one-time work — skip the code entirely and use our browser-based JSON Flattener.
This post collects the idiomatic flatten patterns for each language, plus honest guidance on when browser beats code.
Java — Jackson Recursive Helper
Jackson is the standard JSON library in Java. To flatten nested JSON into dot-notation keys:
import com.fasterxml.jackson.databind.*;
import java.util.*;
public static Map<String, Object> flatten(JsonNode node, String prefix) {
Map<String, Object> result = new LinkedHashMap<>();
node.fields().forEachRemaining(entry -> {
String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey();
JsonNode value = entry.getValue();
if (value.isObject()) {
result.putAll(flatten(value, key));
} else {
result.put(key, value.isValueNode() ? value.asText() : value);
}
});
return result;
}
Call it with flatten(mapper.readTree(jsonString), ""). Arrays are preserved as leaf values (JsonNode), not exploded.
For Gson (older projects), the pattern is similar but uses JsonObject and JsonElement types. No functional difference for the flatten operation.
C# — Newtonsoft.Json (JObject) Walker
Newtonsoft.Json (Json.NET) remains the most common JSON library in .NET. The idiomatic flatten:
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
public static Dictionary<string, object> Flatten(JToken token, string prefix = "") {
var result = new Dictionary<string, object>();
if (token is JObject obj) {
foreach (var prop in obj.Properties()) {
string path = string.IsNullOrEmpty(prefix) ? prop.Name : prefix + "." + prop.Name;
if (prop.Value is JObject) {
foreach (var kv in Flatten(prop.Value, path)) result[kv.Key] = kv.Value;
} else {
result[path] = (prop.Value as JValue)?.Value ?? prop.Value;
}
}
}
return result;
}
For System.Text.Json (modern .NET), the pattern is similar but uses JsonElement. System.Text.Json is faster and no-allocation-heavy; Newtonsoft is more ergonomic. Most teams use Newtonsoft unless perf is a bottleneck.
Go — map[string]interface{} Traversal
Go has no JSON object type; you unmarshal into a generic map[string]interface{} and walk it:
package main
import (
"encoding/json"
"fmt"
)
func flatten(prefix string, src map[string]interface{}, dst map[string]interface{}) {
for k, v := range src {
key := k
if prefix != "" {
key = prefix + "." + k
}
if nested, ok := v.(map[string]interface{}); ok {
flatten(key, nested, dst)
} else {
dst[key] = v
}
}
}
func main() {
var src map[string]interface{}
json.Unmarshal([]byte(raw), &src)
dst := map[string]interface{}{}
flatten("", src, dst)
out, _ := json.MarshalIndent(dst, "", " ")
fmt.Println(string(out))
}
Arrays become []interface{} at leaf positions. If you need struct-based flattening (known types at compile time), reflection is required — more code, more edge cases.
Rust — serde_json Recursion
Rust's serde_json uses a Value enum. Flatten walks the tree and builds a new Map:
use serde_json::{Value, Map};
fn flatten(prefix: &str, src: &Value, dst: &mut Map<String, Value>) {
if let Value::Object(obj) = src {
for (k, v) in obj {
let key = if prefix.is_empty() { k.clone() } else { format!("{}.{}", prefix, k) };
if v.is_object() {
flatten(&key, v, dst);
} else {
dst.insert(key, v.clone());
}
}
}
}
fn main() {
let src: Value = serde_json::from_str(raw).unwrap();
let mut dst = Map::new();
flatten("", &src, &mut dst);
println!("{}", serde_json::to_string_pretty(&Value::Object(dst)).unwrap());
}
Rust's type system makes this a bit more ceremonial, but the logic is identical to the other languages. Arrays preserved as Value::Array at leaves.
Kotlin and Scala — JVM-Based Variants
Kotlin: Use Jackson (with kotlin-jackson-module) — same pattern as Java but with Kotlin idioms:
fun flatten(node: JsonNode, prefix: String = ""): Map<String, Any?> {
val result = mutableMapOf<String, Any?>()
node.fields().forEach { (k, v) ->
val key = if (prefix.isEmpty()) k else "$prefix.$k"
if (v.isObject) result.putAll(flatten(v, key))
else result[key] = if (v.isValueNode) v.asText() else v
}
return result
}
Scala: play-json, circe, and json4s all have similar patterns. No library has a built-in flatten flag; you write the recursive traversal.
The JVM world in general does not provide flatten out of the box. This is part of why the browser tool gets used even by teams that write in JVM languages — it is faster than remembering which library your service uses.
The Browser Tool for Statically-Typed Developers
Statically-typed languages reward you for defining data shapes up front. Flattening breaks that — the flat output loses its type structure. You end up with Map<String, Object>, Dictionary<string, object>, or map[string]interface{}, none of which are type-safe.
For one-off flattens that do not need to integrate with your typed codebase (debugging, sample prep, ad-hoc analysis), the browser tool skips the untyped-map detour entirely. Paste, click, copy — you get flat JSON text, which you can feed into a CSV, a spreadsheet, or a colleague's inbox.
For in-service flattening where the flat output is part of your data pipeline, pick one of the patterns above and build it properly. Common places it matters: ETL services, log normalization, legacy system adapters.
The rule: type the flatten when the output needs to integrate. Skip to the browser when the output is for human consumption.
Flatten JSON Without Writing a Helper
Free browser tool — paste, click, copy. No library, no reflection.
Open Free JSON FlattenerFrequently Asked Questions
Does Jackson have a built-in flatten method?
No — Jackson provides the JsonNode API you need, but the actual flatten helper is something you write once and reuse. Some third-party libraries exist (json-flattener, github.com/wnameless/json-flattener) if you want a dependency.
Is there a native flatten in System.Text.Json?
No. System.Text.Json provides JsonElement for traversal but not flatten itself. Write a recursive helper using JsonElement.EnumerateObject(). Same pattern as Newtonsoft; different type names.
How do I flatten Go structs instead of maps?
Reflection. Use reflect package to walk struct fields. For well-typed flattening, some teams use code generation (stringer-style) to produce flatten methods at build time. For one-off work, unmarshal into map[string]interface{} first — simpler than reflection.
Can the browser tool handle output from my Java or C# service?
Yes — output is just JSON text. Whatever your service emits, paste it into the flattener, the result is the same as running your language-specific flatten helper. Faster for one-off verification.

