Home > database >  C# JsonApiDotNetCore handle int (in DTO) as string in JSON for attribute properties
C# JsonApiDotNetCore handle int (in DTO) as string in JSON for attribute properties

Time:12-14

We've upgrade a C# API with JsonApiDotNetCore from netcore 3.1 to .NET 7. We've also updated the Entity Framework to version 7 and the JsonApiDotNetCore to version 5.1 (all latest at the moment).

Due to some strange things in the React front-end application, we need to handle int's as Strings in the JSON objects. Both for return as posting. This isn't a problem for normal properties, but it is when we want to add the property to the attributes section of the JSON object.

When adding the [Attr] attribute, it's not possible to add a JSON conversion (JsonConverter) to it.

We've also tried to create a work-a-round by duplicating the properties in our DTO, as a String value, for example:

public class MyDTO {
    public int Id { get; set ;}


    [NotMapped]
    public int EmployeeId { get; set ;}

    [NotMapped]
    [Attr(PublicName = "employeeid")]
    public string PublicEmployeeId 
    {
        get => EmployeeId.ToString();
        set 
        {
            if (int.TryParse(value, out int result))
                EmployeeId = result;
        }
    }
}
builder.Property(x => x.PublicEmployeeId)
    .HasConversion(
        // Convert to DB type
        v => int.Parse(v),
        // Convert to model type
        v => v.ToString()
    )
    .HasColumnName("employeeid");

This gives however problems when saving items, probably because the original EmployeeId field is also linked as foreign key. So we get an error the column is used twice in the SQL query created by the JsonApiController.

Above solution also seems a bit hacky one, I think it's better to find a way to just map the original property to the attributes region as a string, but cannot find a way to do so.

With the older Newtonsoft.Json package, this was possible to create a JsonConverter, and checking in the WriteJson method, which property was being written (see code example below), this is not possible anymore as we are forced to use System.Text.Json right now

Code example Newtonsoft:

    /// <summary>
    /// Custom JsonConverter, convert all Id properties in json to string when converting to json.
    /// https://stackoverflow.com/questions/39526057/json-net-serialize-numeric-properties-as-string
    /// </summary>
    public class FormatIntIdsAsStringConverter : JsonConverter
    {
        public override bool CanRead => false;
        public override bool CanWrite => true;
        public override bool CanConvert(Type type) => type == typeof(int);
 
        public override void WriteJson(
            JsonWriter writer, object value, JsonSerializer serializer)
        {
            var path = writer.Path;
            if (!string.IsNullOrEmpty(path))
            {
                var propertyName = path.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Last();
                if (propertyName.Contains("Id"))
                {
                    int number = (int)value;
                    writer.WriteValue(number.ToString(CultureInfo.InvariantCulture));
                    return;
                }
            }
            writer.WriteValue(value);
        }
 
        public override object ReadJson(
            JsonReader reader, Type type, object existingValue, JsonSerializer serializer)
        {
            throw new NotSupportedException();
        }
    }

CodePudding user response:

Do you want to convert the properties of type Int to String in the response data of the API?

You can set the NumberHandling property of JsonOptions:

builder.Services.AddControllers().AddJsonOptions(option =>
{
    option.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.WriteAsString;
});

Of course, registering in Program.cs will convert all properties to String type. If you want to convert a property to String type, you can add [JsonNumberHandling(JsonNumberHandling.WriteAsString)] for this property alone.

In addition, the setting for converting String type to Int is:

builder.Services.AddControllers().AddJsonOptions(option =>
{
    option.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString;
});

Individually set for a property: [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)].

For more details, you can refer to this link.

Hope this can help you.

CodePudding user response:

With help on GitHub, we got it working, by replacing the ResourceObjectConverter like

IJsonApiOptions options = app.ApplicationServices.GetRequiredService<IJsonApiOptions>();
            ResourceObjectConverter existingConverter = options.SerializerOptions.Converters.OfType<ResourceObjectConverter>().Single();
            options.SerializerOptions.Converters.Remove(existingConverter);
            options.SerializerOptions.Converters.Add(new WritePropertyNamesEndingInIdAsStringConverter(existingConverter));

For more information see https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1227

  • Related