JSON in Other Languages: Java, C#, Go and PHP

Intermediate
14 min

JSON in Other Languages: Java, C#, Go and PHP

JSON is language-neutral, but every language maps it onto its own types differently, and statically typed languages prefer classes and structs so the compiler can check field access. In this lesson you will serialize and deserialize the same user document in Java, C#, Go and PHP and learn the conventions and traps of each.

Java: Jackson

Java has no JSON support in the standard library; Jackson is the de facto choice and the default in Spring Boot. ObjectMapper maps JSON onto classes and records:

java
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; public record User(int id, String name, @JsonProperty("email_address") String email, boolean active) {} ObjectMapper mapper = new ObjectMapper(); User user = mapper.readValue(json, User.class); // text -> object String pretty = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user); // object -> text

@JsonProperty renames a field when the JSON key does not match the Java name. By default Jackson throws UnrecognizedPropertyException on keys the class does not declare; disable that with mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) when an API may add fields. Register JavaTimeModule to serialize java.time dates. Gson is the common alternative with the same shape.

C#: System.Text.Json

.NET ships System.Text.Json in the framework. JsonSerializerDefaults.Web selects the settings you want for APIs: camelCase names and case-insensitive matching.

csharp
using System.Text.Json; using System.Text.Json.Serialization; public record User(int Id, string Name, [property: JsonPropertyName("email_address")] string Email, bool Active); var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true }; User user = JsonSerializer.Deserialize<User>(json, options)!; string output = JsonSerializer.Serialize(user, options);

Deserialize<T> returns null for the literal null, hence the !. Unknown properties are ignored. Older codebases use Newtonsoft.Json; JsonNode.Parse(json) gives dictionary-style access to documents without a class.

Go: encoding/json and Struct Tags

Go's standard library handles JSON through json.Marshal and json.Unmarshal, with struct tags controlling the mapping (see the complete program at the top of the lesson):

go
type User struct { ID int `json:"id"` Email string `json:"email,omitempty"` // omitted from output when empty secret string // unexported: never serialized } err := json.Unmarshal([]byte(data), &u) out, err := json.MarshalIndent(u, "", " ")

Only exported (capitalized) fields participate, and without a tag the key is the field name exactly, so ID would produce "ID". Unknown keys are ignored unless you call DisallowUnknownFields() on a json.Decoder, the type used for reading streams such as r.Body. Decoding into map[string]any turns every number into float64; use typed structs or json.Number to keep large integers exact.

PHP: json_encode and json_decode

PHP has JSON built in. json_decode returns stdClass objects by default; pass true as the second argument for associative arrays, which most code prefers:

php
$user = json_decode($json, true, 512, JSON_THROW_ON_ERROR); echo $user['name']; $out = json_encode($user, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);

Without JSON_THROW_ON_ERROR, json_decode returns null on invalid input and you must check json_last_error_msg(), a classic silent failure. Two PHP-specific traps: an empty array encodes as [], so use new stdClass() or JSON_FORCE_OBJECT when the consumer expects {}, and integers beyond 64 bits become floats unless you pass JSON_BIGINT_AS_STRING. PHP 8.3 added json_validate() for syntax checks.

Side by Side

| Language | Library | Text to object | Object to text | Rename a field | |----------|---------|----------------|----------------|----------------| | Java | Jackson | mapper.readValue(json, T.class) | mapper.writeValueAsString(obj) | @JsonProperty | | C# | System.Text.Json | JsonSerializer.Deserialize<T>(json) | JsonSerializer.Serialize(obj) | [JsonPropertyName] | | Go | encoding/json | json.Unmarshal(bytes, &v) | json.Marshal(v) | struct tag | | PHP | built in | json_decode($json, true) | json_encode($value) | array keys |

Two themes recur: every library has a rename mechanism because JSON keys rarely follow the language's naming rules, and none has a native date type, so dates travel as ISO 8601 strings.

Quick Quiz
Question 1 of 3

In Go, why would a struct field named `secret` never appear in JSON output?

Key Takeaways

  • Every language offers the same two operations: deserialize text into typed values and serialize values back to text.
  • Java (Jackson), C# (System.Text.Json) and Go (encoding/json) map JSON onto classes or structs with rename annotations.
  • PHP works with associative arrays; always pass JSON_THROW_ON_ERROR to avoid silent null results.
  • Unknown-field handling, numeric precision and date formats are the details that differ between libraries.
  • Keep JSON keys consistent (camelCase or snake_case) and map them explicitly in each language.

Next lesson: JSON Configuration Files: package.json, tsconfig.json and JSONC — read and write the JSON files that configure real projects, and understand comments, $schema and their limits.

JSON in Other Languages: Java, C#, Go and PHP - JSON | CodeYourCraft | CodeYourCraft