Shrayas
Shrayas

Reputation: 6994

Writing reusable JsonConverters for json.net

I'm querying a service which returns a list of tags in multiple formats:

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

or

{
  "tags": "a b c"
}

or

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

The object that I want to deserialize this to is a List<string>. I've written a TagsConverter that implements the necessary methods in JsonConverter like so:

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 this works, but only for tags that are separated by spaces.

However the TagsConverter can work in all 3 scenarios if I'm able to just change the Split and the Join functions in a parameterized fashion. But since we pass only the type to the JsonConverter attribute, I understand that we can't pass a "delimiter" as an argument there.

Is there any way to achieve this?

Upvotes: 3

Views: 1077

Answers (2)

Nkosi
Nkosi

Reputation: 247098

If only needed for the reading/deserialization of the JSON do note that string.Split can take a character array char[]

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

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

Upvotes: 2

David L
David L

Reputation: 33833

There is an additional JsonConverter constructor overload that takes both a type and a params object[] converterParameters that is injected into the concrete instance of your converter. You can take advantage of this overload to maximize your reusability.

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);
    }
}

Utilizing the overload is as easy as passing it as a second parameter:

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

Upvotes: 3

Related Questions