3

Assuming a json string like the following:

string json = '{"string_property":"foo_bar", ... other objects here ...}';

I was wondering if there's a way to run a transformation on the parsed object so that instead of getting foo_bar, I'll get foo bar after running the following method (can be anything really)

public string Transform(string s) {
    return s.Replace("_"," ");
}

I can manually alter my poco after deserializing, but wondered what would be a "cleaner" approach?

Noctis
  • 11,507
  • 3
  • 43
  • 82
  • 1
    Look at http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm – Callum Linington Jul 13 '16 at 12:34
  • Are you trying to transform also your property names or just the values? And do you want the same transform for all properties – Eric Linde Jul 13 '16 at 12:58
  • Unless you need to alter your property names and you want the same transform for all string properties, the cleanest approach would be to first Deserialize and then transform the values of all string properties using reflection. – Eric Linde Jul 13 '16 at 15:53
  • @EricLinde no, i'm happy leaving all the names as is, i'm trying to clean some html tags from the strings. I guess i could run the method on all strings, even though i know it'll apply to only 2 arrays out of 30-40 objects. – Noctis Jul 13 '16 at 23:17

2 Answers2

5

You can transform your string properties as you deserialize your root object by using a custom JsonConverter targeted at all string type values:

public class ReplacingStringConverter : JsonConverter
{
    readonly string oldValue;
    readonly string newValue;

    public ReplacingStringConverter(string oldValue, string newValue)
    {
        if (string.IsNullOrEmpty(oldValue))
            throw new ArgumentException("string.IsNullOrEmpty(oldValue)");
        if (newValue == null)
            throw new ArgumentNullException("newValue");
        this.oldValue = oldValue;
        this.newValue = newValue;
    }

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var s = (string)JToken.Load(reader);
        return s.Replace(oldValue, newValue);
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then use it like:

var settings = new JsonSerializerSettings { Converters = new[] { new ReplacingStringConverter("_", "") } };
var result = JsonConvert.DeserializeObject<RootObject>(json, settings);

Note however that if individual string-type properties have their own converters applied directly with [JsonConverter(Type)], those converters will be used in preference to the ReplacingStringConverter in the Converters list.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Cheers mate, you've pointed me in the right direction. I'm attached my solution. I thought about removing the properties and dealing with "missing" ones, but it was too much of a hassle for my needs. – Noctis Jul 21 '16 at 11:53
2

I've ended up doing the following:

First, create a converter that only reads and all it does is url decode the string.

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var s = (string)JToken.Load(reader);
        return HttpUtility.UrlDecode(s);
    }

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then, simply add the following to the POCO properties that need to be decoded:

[JsonConverter(typeof(UrlDecoderConverter))]
public string url { get; set; }
Noctis
  • 11,507
  • 3
  • 43
  • 82