2

I want to deserialize a JSON structure like this:

{
    "Name": "Foo",
    "bar": {
        "Name": "Bar",
        "DefaultableProperty": "default"
    }
}

... where it is optional to define "bar". And in the case that it is not defined, I want to load an object of type Bar with some default values.

Here are my class definitions for Foo and Bar

public class Foo
{
    [JsonConstructor]
    public Foo()
    {
        string bardefaults = @"{ ""Name"": ""defaultname""}";
        b = new Bar(bardefaults);
    }
    public string Name { get; set; }
    public Bar bar { get; set; }
}

public class Bar
{
    public Bar() { }
    public Bar(string json)
    {
        Bar copy = JsonConvert.DeserializeObject<Bar>(json);
        Name = copy.Name;
        DefaultableProperty = copy.DefaultableProperty;
    }
    public string Name { get; set; }

    [DefaultValue("default")]
    [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    public string DefaultableProperty { get; set; }
}

And here is my test method for deserializing some JSON:

[TestMethod]
public void CanDeserializeObjectWithDefaultValues()
{
     string s = @"{ 'Name': 'aname', 
                  }";
     Foo foo = JsonConvert.DeserializeObject<Foo>(s);
     Assert.AreEqual("aname", foo.Name);
     Assert.AreEqual("default", foo.bar.DefaultableProperty);
     Assert.IsNotNull(foo.bar.Name);
}

Using the code above I am able to deserialize an object of type Bar with it's default values. However, I would like to be able to use a DefaultValue for Foo.bar as I do for Bar.DefaultableProperty. I haven't been able to achieve this with the following syntax:

So my question is does Json.NET have some support for creation of a default custom object? If not, then what is the best way to approach this situation?

jayant
  • 2,349
  • 1
  • 19
  • 27
  • 1
    I *think* you could make `[DefaultValue(typeof(Bar), @"{ ""Name"": ""bar"" }")]` work with an [appropriate `TypeConverter` and `JsonConverter`](https://stackoverflow.com/questions/31325866) - however, there's a problem: [`DefaultValueAttribute.Value`](https://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.value.aspx) is just an object, and your `Bar` class is *mutable*, so setting it in `Foo` will save a reference to the global instance everywhere - which will then get modified. – dbc Jun 09 '16 at 09:29
  • That being said, you can simplify your code quite a bit by using [`JsonConvert.PopulateObject()`](http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_PopulateObject.htm) rather than deserializing and copying. – dbc Jun 09 '16 at 09:30

1 Answers1

0

What I did is created a new JsonDefaultValueAttribute attribute which derives from DefaultValueAttribute, and added a new ctor to support converting json-string value to target type as follows:

public class JsonDefaultValueAttribute : DefaultValueAttribute
{
    public JsonDefaultValue(string json, Type type) : base (ConvertFromJson(json, type))
    {            
    }

    private static object ConvertFromJson(string json, Type type)
    {
        var value = JsonConvert.DeserializeObject(json, type, new JsonSerializerSettings
        {
            MissingMemberHandling = MissingMemberHandling.Error,
            NullValueHandling = NullValueHandling.Include,
            DefaultValueHandling = DefaultValueHandling.Populate
        });

        return value;
    }
    ....
}

Now you can use the new attribute instead of using the default one.

DeepForest
  • 96
  • 3