R
Rustemsoft LLC
Field Manuals →
Home / Documentation / Serialization-Safe Guide
Rustemsoft
Serialization-Safe Protection Playbook

Serialization Contracts That Survive Obfuscation

When renaming meets System.Text.Json, Newtonsoft.Json, or XmlSerializer, payload fields can turn into silent nulls. This guide shows a contract-first workflow to prevent that-while keeping obfuscation strong.

C# & VB.NET Intermediate ~12 min read • By Rustemsoft engineering
What fails

Three outcomes, one underlying mismatch

Nulls & defaults

No exception-just missing values. JSON deserializers often “skip keys they can’t map.”

Discriminator blow-ups

Polymorphic payloads fail when the discriminator can no longer be resolved.

XML “xsi:type” drift

XML contracts can hinge on type names; renaming changes those identifiers.

The root pattern: the runtime looks up members/types by string identifiers stored in assembly metadata. If obfuscation rewrites those identifiers but serializers still expect the original “wire names”, your contract stops matching.

Mental model

Wire names vs. symbol names

Think of every DTO as governed by two separate naming domains:

Wire contract (partner-owned)

Keys and element names (and polymorphic discriminators like "$type" / xsi:type) are part of the payload shape. Your customers and integrations depend on these strings.

Symbol contract (obfuscator-controlled)

The member and type identifiers stored in your assembly metadata are what serializers consult when you don’t explicitly pin names.

If you rename the symbol contract but the serializer still tries to find the original wire strings, mapping fails. Sometimes it fails loudly; sometimes it fails silently.

Example payload: one message, two kinds of names
{
  "$type": "card",
  "payment_ref": "PAY-20391",
  "Currency": "EUR",
  "last_four": "4242",
  "ExpiryMonth": 12
}
Practice

Keep the wire stable: pin or shield

A

Pin it with attributes

Move serializer-visible names into attribute metadata so renaming can’t break them.

  • [JsonPropertyName] / [JsonProperty]
  • [XmlRoot] / [XmlElement] / [XmlAttribute] / [XmlType]
  • Explicit polymorphic discriminators (avoid “default to type name” behavior)
  • [DataMember(Name = "...")]
B

Shield it by freezing symbols

For legacy DTOs where the wire format rides on bare member names, exclude those types (or namespaces) from renaming. The symbol contract remains identical, so the serializer still finds what it expects.

This keeps the rest of your assembly fully protected: renaming, string encryption, and control-flow protection can remain active where they don’t affect contracts.

Polymorphism is the most fragile surface

Discriminator names are also “wire strings”. If they’re derived from type names, obfuscation can break resolution.

System.Text.Json

Prefer explicit discriminator strings in JsonDerivedType.

Newtonsoft.Json

Avoid brittle TypeNameHandling resolution; constrain using a strict binder/whitelist approach.

XmlSerializer

Pin xsi:type with [XmlType] or shield the type names.

Release gate

Verify the obfuscated artifact (not the source)

The simplest way to avoid “works in smoke tests, fails in production” is to run a contract round-trip against the protected binary.

  1. 1 Commit golden payloads (JSON and XML), including each polymorphic subtype.
  2. 2 Deserialize and assert every property binds to a non-default value.
  3. 3 Round-trip both directions, and compare key sets (format-insensitive).
  4. 4 Test the obfuscated artifact produced by your pipeline.
  5. 5 Fail on silence: a null where a value belongs should break CI.
Example assertion style (C#)
[Fact]
public void Card_payment_binds_every_wire_key()
{
    var dto = JsonSerializer.Deserialize<CardPaymentDto>(Golden.CardPayment);

    Assert.Equal("PAY-20391", dto.PaymentRef);   // pinned by attribute
    Assert.Equal("EUR", dto.Currency);         // pinned/shielded symbol
    Assert.Equal(12, dto.ExpiryMonth);         // pinned/shielded symbol
    Assert.Equal("4242", dto.LastFour);       // pinned by attribute
}
Answers

Questions from the field

Is it safer to disable renaming across my entire assembly? +
You reduce the failure risk, but you also lose a major part of protection quality. Prefer targeted exclusions: keep renaming for business logic and engines, and shield only the DTO namespaces where wire formats depend on member symbol names.
Do [DataContract] and [DataMember] attributes make members rename-safe? +
Only when you use explicit names (for example, [DataMember(Name = "invoice_total")]). If you leave the name unspecified, the serializer still relies on the member symbol name-so you must shield that member instead.
Can string encryption corrupt JSON keys or XML names? +
No. String encryption changes what happens inside methods, while serializer keys for attribute-pinned contracts come from attribute metadata. For un-attributed keys, renaming exclusions are what preserve correctness.
How do I catch broken contracts before release? +
Run the round-trip drill against the protected artifact: deserialize golden fixtures, assert every member binds to expected values, and ensure every discriminator / xsi:type resolves. Don’t assume the development build proves anything-protected renaming is what changes the truth.

Contract-preserving obfuscation isn’t a compromise-it’s the only craft that ships reliably.

Pin wire names, shield legacy DTO symbols, then test the protected binary. Your customers keep getting the same payload shape-your attackers don’t get readable code.

Reflection-Safe Obfuscation in .NET → Keeping Dynamic Code Alive After Protection: preserve dynamic type resolution, serialization, and DI while hardening assemblies with Skater .NET Obfuscator. Reverse-Mapping Obfuscated Stack Traces → An enterprise guide to resolving obfuscated C# and .NET exception logs in telemetry platforms using automated XML symbol map lookup tables and build artifacts. Automating .NET Assembly Protection in Visual Studio & MSBuild → Architectural guide for embedding Opaquer CLI obfuscation into Visual Studio post-build steps and MSBuild execution pipelines. Deterministic .NET Obfuscation: A Release Evidence Playbook → A release-engineering approach to repeatable protected .NET builds, signing, traceability, and CI artifact retention. Protect .NET Secrets from String-Literal Mining → Remove sensitive string literals from managed assemblies: in-assembly string encryption vs native DLL string storage, with ILSpy verification and deployment checks.