简体   繁体   中英

How can I use Json.net to populate a model with custom bindings/mappings?

Here's my JSON:

{
    "macAddress": "000A959D6816",
    "softwareVersion": "1.2.1.5-UnnecessaryInfo",
    "lastUpdated": "2015-04-03 20:46:40.375 -0500",
    "charging": true
}

And using Json.NET, I can do the following in C#:

namespace JsonTest
{
    public class Tablet
    {
        public string MacAddress { get; set; }
        public string SoftwareVersion { get; set; }
        public DateTime LastUpdated { get; set; }
        public bool Charging { get; set; }
    }

    public class TestClass
    {
        public void Test()
        {
            var json = "{ ... }"; // filled in with JSON info from above
            var model = new Tablet();
            try
            {
                JsonConvert.PopulateObject(json, model);
            }
            catch (JsonSerializationException ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

So far, so good. The code I have here works great. It populates my model object with all the data from the Json. However, I don't really want the SoftwareVersion property of my model to be a string; I'd rather have it be an instance of the System.Version class. In other words, I'd like my Tablet class to look more like this:

public class Tablet
{
    public string MacAddress { get; set; }
    public Version SoftwareVersion { get; set; }
    public DateTime LastUpdated { get; set; }
    public bool Charging { get; set; }
}

I don't care about the unnecessary info that gets appended on the end of that version string, so I'd like to write some sort of mapper/binder class that examines the version string from the Json, strips off the unnecessary info, and then parses that field into a Version object before proceeding to populate my model. I do know how to write that individual parsing method; this would do the trick:

private static Version ParseVersion(object versionObj)
{
    var pattern = new Regex(@"^[\d.]+");
    var versionString = versionObj.ToString();
    if (!pattern.IsMatch(versionString)) return null;

    var match = pattern.Match(versionString);
    versionString = match.Groups[0].ToString();

    Version version;
    Version.TryParse(versionString, out version);
    return version;
}

What I don't know is where and how to "plug this in" during the JsonConvert process. I see that the PopulateObject takes an optional JsonSerializerSettings parameter, and in turn that has several different object initializer parameters like Binder and Converters . But I'm not sure which one to use, nor how to write either of those classes to do what I'm describing here. How do I do it? And what is the difference between a Binder and a Converter?

Just add an appropriate JsonConverterAttribute to your Version property, and PopulateObject will use it:

public class Tablet
{
    public string MacAddress { get; set; }
    [JsonConverter(typeof(VersionConverter))]
    public Version SoftwareVersion { get; set; }
    public DateTime LastUpdated { get; set; }
    public bool Charging { get; set; }
}

And here is the actual converter:

public class VersionConverter : JsonConverter 
{
    private static Version ParseVersion(object versionObj)
    {
        var pattern = new Regex(@"^[\d.]+");
        var versionString = versionObj.ToString();
        if (!pattern.IsMatch(versionString)) 
            return null;

        var match = pattern.Match(versionString);
        versionString = match.Groups[0].ToString();

        Version version;
        Version.TryParse(versionString, out version);
        return version;
    }

    public override bool CanConvert(Type objectType)
    {
        return  objectType == typeof(System.Version);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        return ParseVersion((string)token);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var version = (Version)value;
        if (version != null)
            writer.WriteValue(value.ToString());
    }
}

You should now be all set.

Alternatively, if you have a Version property appearing in many different container classes in a complex object graph, and you don't want to set the JsonConverterAttribute everywhere, you can add your converter to JsonSerializerSettings.Converters , then pass that to PopulateObject :

JsonConvert.PopulateObject(json, model, new JsonSerializerSettings { Converters = new JsonConverter [] { new VersionConverter() } } );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM