Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable DateTime in Dictionary can't use custom converter to convert into empty string #2945

Open
kamyaw010 opened this issue Apr 18, 2024 · 3 comments

Comments

@kamyaw010
Copy link

kamyaw010 commented Apr 18, 2024

I have some classes must be stored in a dictionary, but the code can not be uploaded, so I simplify the code and change it to below acts like the actual behavior.

But the Nullable<DateTime> can not trigger the converter and can't be serialize into an empty string, always serialize into null value, but actually I need it is an empty string so the front-end can accept it (front-end is a third-party library that can not be changed and some fields must have unless an empty string than null )

Source/destination types

JsonConvertTestClass Code ( have tried using [JsonConverter] attribute in DateTime? field ):

public record JsonConvertTestClass
{
    public DateTime? GTime { get; set; }
    public ParentClass Parent { get; set; } = new();
}

public record ParentClass
{
    public DateTime? PTime { get; set; }
    public ChildClass Child { get; set; } = new();
}

public record ChildClass
{
    public DateTime? CTime { get; set; }
}

Custom Converter Code (no matter use JsonConverter or JsonConverter<DateTime?>):

public class DateTimeConverter : JsonConverter
{
    public override bool CanWrite => true;

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTime?) || objectType == typeof(DateTime);
    }

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        if (value is null)
        {
            writer.WriteValue(string.Empty);
        }
        else
        {
            writer.WriteValue(value);
        }
    }
}

Serialization Code:

var dict = new Dictionary<string, JsonConvertTestClass>();
for (var i = 0; i < 2; i++)
{
    dict.Add(i.ToString(), new JsonConvertTestClass());
}

Console.WriteLine(JsonConvert.SerializeObject(dict, Formatting.Indented, new JsonSerializerSettings { Converters = { new DateTimeConverter() } }));

Source/destination JSON

{
  "0": {
    "GTime": null,
    "Parent": {
      "PTime": null,
      "Child": {
        "CTime": null
      }
    }
  },
  "1": {
    "GTime": null,
    "Parent": {
      "PTime": null,
      "Child": {
        "CTime": null
      }
    }
  }
}

Expected behavior

{
  "0": {
    "GTime": "",
    "Parent": {
      "PTime": "",
      "Child": {
        "CTime": ""
      }
    }
  },
  "1": {
    "GTime": "",
    "Parent": {
      "PTime": "",
      "Child": {
        "CTime": ""
      }
    }
  }
}

Actual behavior

All DateTime are null

Steps to reproduce

// Even use [JsonConverter] attribute doesn't work
Console.WriteLine(JsonConvert.SerializeObject(dict, Formatting.Indented, new JsonSerializerSettings { Converters = { new DateTimeConverter() } }));
@elgonzo
Copy link

elgonzo commented Apr 18, 2024

The behavior you see is pretty much hard-coded:

private void SerializeValue(JsonWriter writer, object? value, JsonContract? valueContract, JsonProperty? member, JsonContainerContract? containerContract, JsonProperty? containerProperty)
{
if (value == null)
{
writer.WriteNull();
return;
}

As a (past) user of the library myself (only still using it in old legacy projects i didn't migrate to modern .NET), i have to admit that i can't muster much hope for this situation to be improved. That said, feel free to look at Newtonsoft.Json's development activities over the last few years and form your own opinion about it.

A work-around for this limitation is to write a converter that deals with the dictionary itself -- this way, this dictionary converter would then be able to iterate over the dictionary, deal with Nullable<T> values itself directly (thus bypassing hard-coded null checks in the serializer) and write/serialize the dictionary data to the JsonWriter in whatever way you need. If you feel this being too much work or your real situation is more complex and involving not just dictionaries with values from a limited number of relatively simple types, i would strongly suggest you contemplate switching to STJ if there are no roadblocks preventing you from doing that...

@kamyaw010
Copy link
Author

The behavior you see is pretty much hard-coded:

private void SerializeValue(JsonWriter writer, object? value, JsonContract? valueContract, JsonProperty? member, JsonContainerContract? containerContract, JsonProperty? containerProperty)
{
if (value == null)
{
writer.WriteNull();
return;
}

As a (past) user of the library myself (only still using it in old legacy projects i didn't migrate to modern .NET), i have to admit that i can't muster much hope for this situation to be improved. That said, feel free to look at Newtonsoft.Json's development activities over the last few years and form your own opinion about it.

A work-around for this limitation is to write a converter that deals with the dictionary itself -- this way, this dictionary converter would then be able to iterate over the dictionary, deal with Nullable values itself directly (thus bypassing hard-coded null checks in the serializer) and write/serialize the dictionary data to the JsonWriter in whatever way you need. If you feel this being too much work or your real situation is more complex and involving not just simple dictionaries, i would strongly suggest you contemplate switching to STJ if there are no roadblocks preventing you from doing that...

For some reasons, can't switch back to STJ :(
Seems can just hold a converter for the dictionary otherwise change this field into string? and handle it myself
Thanks.

@elgonzo
Copy link

elgonzo commented Apr 18, 2024

For some reasons, can't switch back to STJ :(

That's unfortunate :-( because this would be easy-peasy to do in STJ https://dotnetfiddle.net/RDmNxD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants