Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON deserialize to constructed protected setter array

I use Newtonsoft JSON to serialize/deserialize my objects. One of those contains an array with a protected setter because the constructor build the array itself and only the members are manipulated.

This can be serialized without problem but when it comes to deserialization the property it is ignored because it is not public. I tried a custom converter with it is also not called because it is not public.

This is a minimized example:

public static class TestCoordsDeserialization
{
    private class Coords
    {
        public Double X { get; set; }
        public Double Y { get; set; }
        public Double Z { get; set; }
        public Double A { get; set; }
    }

    private class Engine
    {
        public string Text { get; set; }
        public int Id { get; set; }
        public Coords[] Outs { get; protected set; }

        public Engine()
        {
            this.Outs = new Coords[3];
            for (int i = 0; i < this.Outs.Length; i++)
            {
                this.Outs[i] = new Coords();
            }
        }
    }

    public static void Test()
    {
        Engine e = new Engine();
        e.Id = 42;
        e.Text = "MyText";
        e.Outs[0] = new Coords() { A = 0, X = 10, Y = 11, Z = 0 };
        e.Outs[1] = new Coords() { A = 0, X = 20, Y = 22, Z = 0 };
        e.Outs[2] = new Coords() { A = 0, X = 30, Y = 33, Z = 0 };
        string json = JsonConvert.SerializeObject(e);
        Console.WriteLine(json); //{"Text":"MyText","Id":42,"Positions":{"Test":9,"Outs":[{"X":10.0,"Y":11.0,"Z":0.0,"A":0.0},{"X":20.0,"Y":22.0,"Z":0.0,"A":0.0},{"X":30.0,"Y":33.0,"Z":0.0,"A":0.0}]}}
        Engine r = JsonConvert.DeserializeObject<Engine>(json);
        double value = r.Outs[1].X; // should be '20.0'
        Console.WriteLine(value);
        Debugger.Break();
    }
}

How can I make value to be 20.0?

like image 678
ZoolWay Avatar asked Sep 20 '14 15:09

ZoolWay


2 Answers

Try this:

var contractResolver = new DefaultContractResolver();
contractResolver.DefaultMembersSearchFlags |= BindingFlags.NonPublic;
Engine r = JsonConvert.DeserializeObject<Engine>(json), new JsonSerializerSettings
            {
                ContractResolver = contractResolver
            });

Using above-mentioned code you will get the follwoing Warning message:

'DefaultContractResolver.DefaultMembersSearchFlags' is obsolete: 'DefaultMembersSearchFlags is obsolete. To modify the members serialized inherit from DefaultContractResolver and override the GetSerializableMembers method instead.'

To solve it you can use the following solution instead:

Create a class that inherits from DefaultContractResolver to get the non public properties:

public class CustomContractResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(
            MemberInfo member,
            MemberSerialization memberSerialization)
        {
            var prop = base.CreateProperty(member, memberSerialization);

            if (!prop.Writable)
            {
                var property = member as PropertyInfo;
                if (property != null)
                {
                    var hasNonPublicSetter = property.GetSetMethod(true) != null;
                    prop.Writable = hasNonPublicSetter;
                }
            }

            return prop;
        }
    }

And finally use it as follow:

var contractResolver = new CustomContractResolver();
Engine r = JsonConvert.DeserializeObject<Engine>(json), new JsonSerializerSettings
            {
                ContractResolver = contractResolver
            });
like image 145
Vahid Farahmandian Avatar answered Sep 18 '22 19:09

Vahid Farahmandian


Mark Outs with the [JsonProperty] attribute:

    private class Engine
    {
        public string Text { get; set; }
        public int Id { get; set; }
        [JsonProperty]  // Causes the protected setter to be called on deserialization.
        public Coords[] Outs { get; protected set; }

        public Engine()
        {
            this.Outs = new Coords[3];
            for (int i = 0; i < this.Outs.Length; i++)
            {
                this.Outs[i] = new Coords();
            }
        }
    }
like image 41
dbc Avatar answered Sep 19 '22 19:09

dbc