4

I am trying to migrate from Newtonsoft.Json to System.Text.Json However, I ran into a problem since I was using DefaultContractResolver. My "custom" behaviour have these rules for property serialization:

  1. Skip property serialization if it is marked with ReadOnly attribute
  2. Skip property serialization in case of null (this is supported)
  3. Skip property serialization which would serialize into an empty object

Example:

class Car
{
  [ReadOnly]
  public string Id { get; set; }

  public string Name { get; set; }

  public Person Owner { get; set; }
}

class Person
{
  [ReadOnly]
  public string Id { get; set; }

  public string Name { get; set; }
}

Now, imagine, we have this data if no rules would apply.

{
   "Id":"1234",
   "Name":"Skoda",
   "Owner":{
      "Id":"abcd",
      "Name":null
   }
}

Now, if I serialize the object, I would like to get this instead.

{
   "Name":"Skoda"
}
Thorberg
  • 93
  • 6
  • Welcome to StackOverflow! Just out of curiosity how did you solve this with Json.Net? – Peter Csala Oct 27 '21 at 08:47
  • BTW in case of System.Text.Json you have the following settings [`JsonSerializerOptions.IgnoreReadOnlyProperties`](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.ignorereadonlyproperties?view=net-5.0) and [`JsonSerializerOptions.IgnoreNullValues`](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.ignorenullvalues?view=net-5.0) – Peter Csala Oct 27 '21 at 08:49
  • 1
    In Json.NET I have overriden CreateProperty method of DefaultContractResolver. This way I set the ShouldSerialize flag. I am actually trying to deserialized the property first to know if it's gonna be empty, using the same rules. Naturally, it is not good for performance, but it is fine for my solution. – Thorberg Oct 27 '21 at 10:21
  • *CORRECTION*: I meant, I SERIALIZE the property first to know if it's gonna be empty ( resulting in "{ }" json) – Thorberg Oct 27 '21 at 10:28
  • The IgnoreReadOnlyProperties is a good idea, but would not be 100% accurate. Sometimes, I may have a read-only property (with private setter), but internally, still need to serialize it. That's why I have the custom attribute, which is independent from the property get/set access.. also, same problem as with JsonIgnore, I need this behaviour only for serialization, not deserialization. – Thorberg Oct 27 '21 at 10:36
  • 1
    There is no easy way to do all of with System.Text.Json because its metadata is private. See [System.Text.Json API is there something like IContractResolver](https://stackoverflow.com/q/58926112/3744182), [Open up metadata infrastructure of System.Text.Json #34456](https://github.com/dotnet/runtime/issues/34456) [Equivalent of DefaultContractResolver in System.Text.Json #31257](https://github.com/dotnet/runtime/issues/31257), – dbc Oct 27 '21 at 14:54
  • Then I guess I'll have to wait for .NET 7 before migrating. Seems like the IContractResolver equivalent could be implement eventually - https://github.com/dotnet/runtime/issues/36785 – Thorberg Oct 27 '21 at 16:31
  • 1
    You might be able to solve the problem with by using Dahomey.Json: https://github.com/dahomey-technologies/Dahomey.Json#conditional-property-serialization – mabs Nov 17 '21 at 15:38

1 Answers1

0

In order to ignore individual properties, you need to use the [JsonIgnore] attribute along with one of these conditions:

  • Always;
  • Never;
  • WhenWritingDefault;
  • WhenWritingNull.

You can also define a default ignore condition through the JsonSerializerOptions type.

If additional behavior is needed, you should write a custom converter.

Example:

class Person
{
  [JsonIgnore(Condition = JsonIgnoreCondition.Always)]
  public string Id { get; set; }

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

More information:

How to ignore properties with System.Text.Json

How to write custom converters for JSON serialization (marshalling) in .NET

Ivo
  • 396
  • 1
  • 9
  • thank you, but the [JsonIgnore] won't solve my issues. I need it to only ignore for serialization, not deserialization. I would like to add custom converter, but what I need is to add custom converter for any type. Basically, for any type, check if it is empty (would serialize to "{ }") or has specific attribute, if yes, skip it, if not, apply the standard converter. – Thorberg Oct 27 '21 at 10:24