# Serialization and Obfuscation

# Overview

Use this guide for JSON serialization with System.Text.Json or Newtonsoft.Json, and for XML or other serialized formats that depend on stable names.

.NET Reactor automatically detects many common serialization patterns and tries to keep serialized data compatible after obfuscation. It recognizes metadata and conventions used by common serializers, including types marked as serializable, attributes from System.Runtime.Serialization, JSON-related attributes, and Newtonsoft.Json patterns such as JsonProperty and ShouldSerialize<PropertyName>.

Automatic detection cannot cover every serialization scenario. A serializer may discover members dynamically, use a custom contract resolver, read names from configuration, or exchange data with another application that expects an exact schema. In these cases, exclude the affected type or member names from renaming.

# When is an exclusion required?

Consider an explicit exclusion when the protected application:

  • fails only after obfuscation while serializing or deserializing
  • produces JSON, XML, or another payload with unexpected property names
  • cannot read data created by an earlier, unobfuscated, or separately protected version
  • exchanges data with a server, database, plug-in, or third-party application that expects fixed names
  • uses reflection, a custom contract resolver, or serializer configuration that .NET Reactor cannot inspect statically
  • stores fully qualified or assembly-qualified type names in the serialized data

Exclude only the smallest required scope. Start with the affected members, then the containing type. Use a broader exclusion only when necessary, and verify that it covers the affected types. Exclusions > Serializable Types covers only types marked as serializable, not every type used for serialization.

# Recommended options

Situation Recommended solution
One or a few classes are affected and their source code is available Add ObfuscationAttribute to the affected types or members
Source code must not be changed Create an Exclude Renaming rule in Advanced Rules
Types marked as serializable must retain their names Enable Exclusions > Serializable Types. This setting covers only marked types.
Affected serialization models are not marked as serializable Use targeted attributes or Advanced Rules. Serializable Types does not cover these models.
The build is automated Save the settings in a project file or use the command line

# Option 1: Use ObfuscationAttribute in source code

This is usually the clearest and most maintainable solution because the compatibility requirement stays next to the serialization model.

The examples below intentionally omit Feature. In .NET Reactor, this is a special case that excludes renaming even when Declarative Protection is disabled. See the attribute reference for this behavior.

# Exclude a type and all its members from renaming

using System.Reflection;

[Obfuscation(Exclude = true, ApplyToMembers = true)]
public sealed class CustomerDto
{
    public string Id { get; set; }
    public string DisplayName { get; set; }
}

ApplyToMembers = true preserves the type name and the names of its members. This is appropriate when a serializer uses both names.

# Exclude only a type name

using System.Reflection;

[Obfuscation(Exclude = true, ApplyToMembers = false)]
public sealed class MessageEnvelope
{
    public object Payload { get; set; }
}

Use this form when only the type identity must remain stable and member names may still be changed.

# Exclude individual members

using System.Reflection;

public sealed class LoginRequest
{
    [Obfuscation(Exclude = true)]
    public string UserName { get; set; }

    [Obfuscation(Exclude = true)]
    public string Password { get; set; }
}

This keeps the selected serialized names stable while allowing the rest of the type to be renamed.

# Option 2: Create an Advanced Rule

Advanced Rules are useful when source code is unavailable or should remain independent of the protection configuration.

  1. Open 2. Protection Settings > Advanced Rules.
  2. Set Enabled to True.
  3. Open Rules and select Add > Exclude Renaming.
  4. Under Where, select the assembly, namespace, and type.
  5. Select the names that must remain unchanged: Type Names, Namespaces, Methods, Fields, Properties, and/or Events.
  6. Enter specific member names where possible. Leaving a selected member category empty applies the rule to all members in that category.
  7. Leave Use Regular Expressions disabled for literal names. Enable it only when a pattern is intentionally required.

For a typical JSON DTO, select Type Names and Properties. Add Fields when fields are serialized directly. Preserve Namespaces only if the serialized format stores fully qualified type names or an external consumer depends on the namespace.

Rules can also target types or members by attribute. The rule editor provides Type Has Attribute and Member Has Attribute, and type modifiers include +serializable. These filters are helpful when the same serialization marker is used across many models. Verify that the affected models actually match the selected filter. For unmarked models, select the affected types directly by namespace and type name instead of relying on a serialization marker.

The rule editor can generate assembly-level ObfuscationAttribute declarations through Assembly Attributes > Generate Assembly Attributes For Selected Rules or Generate Assembly Attributes For All Rules.

See the Advanced Rules Editor for the interface, filters, and additional examples. Generated attributes use Feature selections. Enable Declarative Protection when applying them from source code.

# Option 3: Exclude types marked as serializable

Enable:

2. Protection Settings > Obfuscation > Exclusions > Serializable Types

This option excludes types explicitly marked as serializable from renaming. A type is not covered by this setting merely because the application serializes it. Use it when the affected types carry the serializable marker and targeted exclusions would be impractical.

Because this setting can preserve many names, targeted attributes or rules generally provide stronger protection. See also the Obfuscation settings.

# Option 4: Configure automated builds

The project-file setting and command-line switch for Serializable Types have the same scope as the GUI option: they apply only to types marked as serializable. Keep targeted attributes or saved Advanced Rules for affected unmarked models.

# .NET Reactor project file

The corresponding settings in a .nrproj project file are located in Protection_Settings. The following is a settings fragment, not a complete project file:

<Protection_Settings>
  <Exclude_Serializable_Types>true</Exclude_Serializable_Types>
  <Include_Compiler_Serializable_Types>false</Include_Compiler_Serializable_Types>
</Protection_Settings>

Use the .NET Reactor GUI to create and save Advanced Rules whenever possible. This avoids manually constructing the rule format and keeps exact type/member selection easy to review.

# Command line

To enable the same renaming exclusion for types marked as serializable:

-exclude_serializable_types 1

Advanced Rules saved in the project file are applied when rule processing is enabled. Load the saved project with -project. The GUI's Command-line > Generate Command-line command can produce the complete command for the current project settings. See Command Line Parameters.

# Troubleshooting workflow

  1. Reproduce the problem with the unobfuscated and obfuscated builds using the same payload.
  2. Compare serialized property, field, type, and namespace names.
  3. Temporarily exclude the affected type and member names from renaming using ObfuscationAttribute or an Advanced Rule. Exclusions > Serializable Types is an alternative diagnostic step only when the affected types are marked as serializable.
  4. If the problem disappears, narrow the exclusion to the names required by the serialization contract. If Serializable Types alone does not help, renaming may still be the cause: unmarked types are not covered by that setting and must be tested with targeted exclusions.
  5. Test both directions: serialize with the old build and deserialize with the new build, then reverse the test.
  6. Test payloads that contain derived types, generic types, collections, null values, and optional members.
  7. If names look correct, also check constructor requirements, metadata removal, reflection usage, and custom converter or contract-resolver logic. See Reflection Problems and, when trimming is part of the build, Trimming Problems.

Run these checks against the actual protected application or library, not a test build that recompiles the original source. See Verify protected output.

# Important notes

  • An exclusion protects future builds but does not repair data already written with incompatible obfuscated names.
  • If a payload contains type information, preserving property names alone may not be enough. Preserve the type name and, when required, its namespace.
  • Explicit serializer names such as JsonPropertyName, JsonProperty, DataMember(Name = ...), or XML serialization names define a more stable external contract. They are useful even when obfuscation exclusions are also applied.
  • Always test compatibility with real production-format data. Serializer behavior can depend on runtime options that are not visible during static analysis.

# Related topics

Advanced Rules Editor · Declarative Protection · Reflection problems · Protect a library · Command Line Parameters