Obfuscating Newtonsoft.Json types: pin property and type names
Obfuscators rename members and types, and Json.NET reads those CLR names to write JSON keys and $type discriminators, so payloads change without any error. The article explains how to pin wire names with [JsonProperty], [EnumMember] and a stable ISerializationBinder, and how to test the protected build.
Key points
- Give every serialized member an explicit [JsonProperty] name; an unattributed property falls back to its renameable CLR name.
- For immutable DTOs deserialized through [JsonConstructor], confirm your obfuscator leaves constructor parameter names alone.
- When using StringEnumConverter, add [EnumMember(Value = ...)] to every enum member so wire values do not depend on member names.
- Do not rely on TypeNameHandling's default $type values; use a custom ISerializationBinder with stable logical names for polymorphic payloads.
- Run the serialization round-trip test against the obfuscated assembly in CI, not only the debug build.
Obfuscation can quietly break Newtonsoft.Json serialization. Json.NET has no compile-time generator, so it reflects over live types and uses the CLR member name as the JSON key. Once a renamer changes that name, the wire format changes too, and stored or shared JSON fails at runtime, where no build step notices.
The article's fixes are explicit [JsonProperty] names on every serialized member, a [JsonConstructor] whose parameter names survive renaming, and [EnumMember] values for string enums. The biggest risk is TypeNameHandling, whose $type value embeds the assembly-qualified type name. A custom ISerializationBinder that maps stable logical names to types solves that, and a round-trip test against the protected build in CI catches regressions.
Real-world use
- A web API whose JSON responses are consumed by a separate frontend, where obfuscating the server build must not change the keys.
- A service that stores polymorphic payment or message documents and reads them back after the next release.
- A queue-based messaging system where producers and consumers are built and obfuscated separately.
Why it matters
In an obfuscated Json.NET project, every serialized name must be pinned explicitly, or stored and shared data will break without any compile error.
Related stories
- Visual Studio October update: compare commits to your working tree and bulk-resolve conflictsVisual Studio Blog6 d ago
- C# 14 extension members: the shadowing bug the compiler won't warn you aboutDEV Community: #csharp1 d ago
- LLM tokens measured in C#: why AI can't count the R's in strawberryDEV Community: #csharp2 d ago