Skip to content
Happy Programming Guide
Start learning
C#

JsonPropertyName and JsonIgnore Not Working in C#

The attribute is there and the JSON still uses the wrong name, or the ignored property still appears? You are almost certainly mixing System.Text.Json and Newtonsoft attributes.

A laptop computer on a table

Nine times out of ten, a [JsonPropertyName] or [JsonIgnore] attribute that is “not working” belongs to a different JSON library than the one doing the serialising. .NET has two: System.Text.Json, built in, and Newtonsoft.Json, the older package. Their attributes have similar names, live in different namespaces, and each library ignores the other’s. Fix the namespace and the attribute starts working.

Two libraries, two attribute sets#

Purpose System.Text.Json Newtonsoft.Json
Namespace System.Text.Json.Serialization Newtonsoft.Json
Rename a property [JsonPropertyName("x")] [JsonProperty("x")]
Skip a property [JsonIgnore] [JsonIgnore]
Serialise JsonSerializer.Serialize(obj) JsonConvert.SerializeObject(obj)

Note that [JsonIgnore] exists in both, with the same name. That is the trap: your using line decides which one you get, and the compiler cannot warn you because both are valid.

Step 1: which serialiser is running?#

Look at the code that produces the JSON, not the model:

  • JsonSerializer.Serialize or JsonSerializer.Deserialize — System.Text.Json.
  • JsonConvert.SerializeObject or JObject — Newtonsoft.
  • ASP.NET Core controllers and minimal APIs — System.Text.Json by default, unless AddNewtonsoftJson() is called in Program.cs.
  • HttpClient.GetFromJsonAsync and PostAsJsonAsync — System.Text.Json.
  • Older libraries, Azure Functions v3, some SDKs — often Newtonsoft.

Then look at the top of the model file. If the serialiser is System.Text.Json and the file says using Newtonsoft.Json;, that is the bug.

CSHARP
using System.Text.Json.Serialization;     // must match the serialiser in use

public class User
{
    [JsonPropertyName("user_name")]
    public string UserName { get; set; } = "";

    [JsonIgnore]
    public string PasswordHash { get; set; } = "";
}

Other causes, once the namespace is right#

The property is not public, or has no setter#

System.Text.Json serialises public properties only. A private property, a public field, or a property with only a getter is skipped regardless of attributes. For fields, opt in with [JsonInclude] or IncludeFields = true in the options. Read-only properties serialise but do not deserialise unless you use an init setter or a constructor.

The attribute is on an interface or base class, but the runtime type differs#

Attributes are read from the declared type being serialised. Serialising a List<object> loses the derived-type attributes; serialise the concrete type or use the polymorphism attributes.

Source generation is in use#

If the project uses JsonSerializerContext for AOT or performance, the attributes must be visible at compile time to the generated code. Adding an attribute and not rebuilding, or having the model in a project the context cannot see, silently reverts to defaults.

JsonIgnore with a condition#

System.Text.Json’s [JsonIgnore] takes a Condition. The default is Always, but WhenWritingNull and WhenWritingDefault mean the property still appears when it has a value — which looks like the attribute being ignored if you expected it always to be omitted.

CSHARP
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Nickname { get; set; }

Global naming instead of attributes#

If every property needs renaming to snake_case or camelCase, do it once in the options rather than on each property:

CSHARP
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
    PropertyNameCaseInsensitive = true,
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};

var json = JsonSerializer.Serialize(user, options);
CSHARP
// ASP.NET Core: apply to every response
builder.Services.ConfigureHttpJsonOptions(o =>
    o.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower);

A [JsonPropertyName] on an individual property overrides the policy, so the two combine cleanly.

Deserialising: names must match exactly#

By default System.Text.Json is case-sensitive when reading. JSON with "username" does not populate a property named UserName unless you set PropertyNameCaseInsensitive = true or add the attribute with the exact incoming name. ASP.NET Core sets case-insensitivity for you; console applications do not.

Pick one library#

Mixed projects are where this problem lives. Choose System.Text.Json for anything new; it is faster, built in and what the framework uses. Keep Newtonsoft only where a dependency requires it, and in those files be explicit:

CSHARP
using STJ = System.Text.Json.Serialization;
using NSJ = Newtonsoft.Json;

public class Legacy
{
    [STJ.JsonPropertyName("id")]
    [NSJ.JsonProperty("id")]
    public int Id { get; set; }
}

Aliasing the namespaces makes the choice visible on every attribute and stops the wrong autocomplete from ever compiling silently.

Questions people ask#

Why does ASP.NET Core ignore my Newtonsoft attributes?

Because it uses System.Text.Json since .NET Core 3.0. Either switch the attributes or call AddNewtonsoftJson() to switch the serialiser back.

Does JsonPropertyName affect deserialisation too?

Yes. The name is used in both directions.

Can I rename a property for output only?

Not with a single attribute. Use a separate DTO for output, or a custom converter.

Which is faster?

System.Text.Json, particularly with source generation. Newtonsoft’s advantage is flexibility with unusual JSON shapes.

Where to go next#

ASP.NET Core error handling with ProblemDetailsRead next

Keep reading

C#

What’s New in C# 14 and .NET 10

.NET 10 is the current long-term release and C# 14 comes with it: extension members, the field keyword, file-based apps and more.…

4 min read

C#

C# Advanced Language Features

LINQ, generics, async/await, pattern matching, records and extension methods — the C# features that make everyday code shorter, explained one at a…

4 min read

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *