Search code examples
c#json.net-coresystem.text.json

How to ignore Property on JSON deserialization?


My Dto has Id property:

  public class ADto
  {
      public int Id { get; set; }
      public string Text { get; set; }
  }

The Id is set by EF Core not by Frontend request, so I want to ignore it on deserialization ( but do not ignore on serialization, so already set Id by EF Core will be returned to Frontend), how to achieve that with attributes? I am using using System.Text.Json.Serialization;.

Deserialization is done by ASP. NET Core, I need to implement it as an attribute.


Solution

  • There is no way to achieve exactly what you want with the attributes built in to System.Text.Json as of .NET 8 or earlier.

    As a workaround, in .NET 7 and later you could create a custom attribute, then check for it in a custom typeInfo modifier when generating contracts for your types.

    First define the following attribute:

    [System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true)]
    public sealed class JsonIgnoreOnDeserializationAttribute : System.Text.Json.Serialization.JsonAttribute
    {
        public static void Apply(JsonTypeInfo typeInfo)
        {
            if (typeInfo.Kind != JsonTypeInfoKind.Object)
                return;
            foreach (var property in typeInfo.Properties)
            {
                if (property.AttributeProvider?.IsDefined(typeof(JsonIgnoreOnDeserializationAttribute), true) == true)
                    property.Set = null;
            }
        }
    }
    

    And modify your type as follows:

    public class ADto
    {
        [JsonIgnoreOnDeserialization]
        public int Id { get; set; }
        public string Text { get; set; }
    }
    

    Then, when setting up your serialization options, apply the modifier in .NET 8 by calling WithAddedModifier() like so:

    var options = new JsonSerializerOptions
    {
        // If you are using source generation, use SourceGenerationContext.Default here instead.
        TypeInfoResolver = new DefaultJsonTypeInfoResolver() 
            .WithAddedModifier(JsonIgnoreOnDeserializationAttribute.Apply),
        // Add other options as required, e.g.:
        WriteIndented = true,
    };
    

    Demo fiddle #1 here.

    Notes:

    • The extension method WithAddedModifier() is new in .NET 8 and allows for modification of contracts of arbitrary IJsonTypeInfoResolver instances, including both both DefaultJsonTypeInfoResolver and JsonSerializerContext.

      In .NET 7, use DefaultJsonTypeInfoResolver and set the modifier explicitly:

      var options = new JsonSerializerOptions
      {
          TypeInfoResolver = new DefaultJsonTypeInfoResolver() 
          {
              Modifiers = { JsonIgnoreOnDeserializationAttribute.Apply },
          },
          // Add other options as required, e.g.:
          WriteIndented = true,
      };
      

      Demo fiddle #2 here.

    • .NET 8 also has the ability to serialize properties that are entirely nonpublic, so you could consider modifying ADto by marking Id with [JsonIgnore] and adding a private get-only surrogate property like so:

      public class ADto
      {
          public ADto() { }
      
          [JsonInclude, JsonPropertyName("Id")]
          private int DeserializedId => Id;
      
          [JsonIgnore]
          public int Id { get; set; }
          public string Text { get; set; }
      }
      

      In .NET 7 and earlier the surrogate property would need to be public, which you probably would not want.

      Demo fiddle #3 here.