Writing Reusable JsonConverters for json.net

I am requesting a service that returns a list of tags in several formats:

{
  "tags": "a,b,c"
}

      

or

{
  "tags": "a b c"
}

      

or

{
  "tags": "a+b+c"
}

      

The object I want to deserialize this for is List<string>

. I wrote a TagsConverter

that implements the required methods in the JsonConverter

following way:

public class TagsConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(string));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return reader
                .Value
                .ToString()
                .Split(' ')
                .ToList();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var list = (List<string>)value;
        var delimitedList = string.Join(" ", list);

        writer.WriteValue(delimitedList);
    }
}

      

Then I annotate the field in the object like so:

public class Foo
{
  [JsonConverter(typeof(TagsConverter))]
  public List<string> Tags { get; set; }
}

      

And it works, but only for tags separated by spaces.

However, it TagsConverter

can work in all three scenarios if I can just change the functions Split

and in a Join

parameterized manner. But since we are only passing the type to the attribute JsonConverter

, I understand that we cannot pass the "separator" as an argument.

Is there a way to achieve this?

+3


source to share


2 answers


There is an additional JsonConverter

constructor overload that takes both a type and a params object[] converterParameters

, which is injected into a specific instance of your converter. You can take advantage of this overload to maximize reuse.

public class TagsConverter : JsonConverter
{
    private readonly string _delimiter;

    public TagsConverter(string delimiter)
    {
        _delimiter = delimiter; 
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(string));
    }

    public override object ReadJson(JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
    {
        return reader
                .Value
                .ToString()
                .Split(_delimiter.ToCharArray())
                .ToList();
    }

    public override void WriteJson(JsonWriter writer, object value, 
        JsonSerializer serializer)
    {
        var list = (List<string>)value;
        var delimitedList = string.Join(_delimiter, list);

        writer.WriteValue(delimitedList);
    }
}

      



Using an overload is as simple as passing it as a second parameter:

public class Foo
{
    [JsonConverter(typeof(TagsConverter), " ")]
    public List<string> Tags { get; set; }
}

      

+3


source


If only read / deserialize JSON is required, please note that it string.Split

can accept a character arraychar[]

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    return reader
            .Value
            .ToString()
            .Split(new char[]{' ', ',', '+'}, StringSplitOptions.RemoveEmptyEntries)
            .ToList();
}

      



This means that the converter can handle all three delimiters when reading.

+2


source







All Articles