Is it possible to restrict JsonConverter registered on a property to only run during model binding?

I have a Web API application that serves an endpoint: POST: /api/Partition and accepts a JSON object with the following structure:

{
    // myProperty is a string array
    myProperty: [ "a", "b" ]
}

However, the corresponding WebAPI controller action accepts a class with the following structure:

[HttpPost]
public ActionResult<MyClass> Post(MyClass postObject)
{
    ...
}

public class MyClass 
{
    public string MyProperty { get; set;}
}

I needed to convert MyProperty from a string[] to a string. Therefore I wrote this custom JsonConverter and registered it to MyProperty.

public class StringToStringArrayJsonConverter : JsonConverter<string>
{
    public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            List<string> list = new List<string>();
            reader.Read();
            while (reader.TokenType != JsonTokenType.EndArray)
            {
                list.Add(JsonSerializer.Deserialize<string>(ref reader, options)!);
                reader.Read();
            }
            return string.Join(",", list);
        }
        return reader.GetString();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        writer.WriteStartArray();
        writer.WriteStringValue(value);
        writer.WriteEndArray();
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return true;
    }
}

// Register JsonConverter on MyProperty
public class MyClass 
{
    [JsonConverter(typeof(StringToStringArrayJsonConverter))]
    public string MyProperty { get; set;}
}

However, I would like this JsonConverter to only take effect during model binding before the controller action runs. Right now, it is also used whenever I use
JsonSerializer.Serialize() and JsonSerializer.Deserialize(), and I don’t want that to happen. How do I override the registered JsonConverter whenever I want to use the JsonSerializer?

  • I’m not entirely sure I understand your question. Are you asking how to make a JsonConverter<T> use custom deserialization but fall back to default serialization? If so see How to use default serialization in a custom System.Text.Json JsonConverter. Or are you asking how to make asp.net-core generate different contracts for model binding and formatting, so that the [JsonConverter(typeof(StringToStringArrayJsonConverter))] attribute is completely ignored for the formatting contract?

    – 




  • @dbc Your second point, basically. I want my custom JsonConverter to only be used for model binding and formatting, not for serialization.

    – 

  • Then why do you only want StringToStringArrayJsonConverter to take effect during deserialization? As currently written your Write() method will do the same thing as default serialization, won’t it?

    – 

  • 2

    This kinda feels like you are just trying to bend the MyClass object to do too many things. Just have different classes and all this complication goes away.

    – 




  • 2

    @Sharon you should prevent using the same class for request and response then. The problem with many mappings can’t be that bad that what you try to achive here will be any better.

    – 

Leave a Comment