.NET November 16, 2025 • 4 min read • JSON

Pragmatic JSON handling in .NET

By Omarr • Published: November 16, 2025

JSON is the default wire format for most of my services: HTTP APIs, queue messages, and configuration. The tooling in modern .NET is good, but it’s still easy to end up with fragile models or painful migrations. Here’s how I keep it boring and predictable.

1. Treat JSON contracts as public APIs

Even if a message is “internal”, I treat its JSON shape as an API contract:

  • Prefer stable names over clever abbreviations.
  • Avoid renaming or removing fields once they’re live.
  • Add new fields as optional with sensible defaults.

2. Use dedicated DTOs, not your EF entities

I almost never serialize EF entities directly. Instead I use explicit DTOs:

  • Entities can evolve for persistence without breaking wire contracts.
  • DTOs can be tuned for clients (flattened, renamed, grouped).
  • Versioning and compatibility logic lives in mapping code, not “magic”.

3. System.Text.Json with explicit options

I stick with System.Text.Json and configure options centrally:

  • Case-insensitive property matching for input.
  • Consistent naming policy (camelCase on the wire).
  • Converters for tricky types (enums, DateTime, value objects).

4. Plan for “unknown” data

When integrating with external systems, I often include a catch-all:

  • An IDictionary<string, JsonElement> for extensions.
  • A raw JSON payload column in SQL for debugging and replays.

That way, if a partner adds a field, my service doesn’t instantly fall over.

5. Log the right amount of JSON

Logging full payloads in production is tempting but dangerous (PII, log volume). I prefer:

  • Logging only keys needed to debug (IDs, a few key fields).
  • Redacting obvious sensitive fields.
  • Sampling full payload logs in non-prod environments instead.

← Back to all articles