JSON десериализуется в построенный защищенный сеттер

Я использую Newtonsoft JSON для сериализации/десериализации моих объектов. Один из них содержит массив с защищенным установщиком, потому что конструктор строит сам массив, и только члены манипулируют.

Это может быть сериализовано без проблем, но когда дело доходит до десериализации, это свойство игнорируется, потому что оно не является общедоступным. Я попробовал собственный конвертер, и он также не вызывается, потому что он не является общедоступным.

Это минимизированный пример:

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

Как сделать value равным 20.0?

Ответ 1

Отметьте Outs атрибутом [JsonProperty]:

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

Ответ 2

Попробуй это:

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

Используя вышеупомянутый код, вы получите следующее предупреждение:

DefaultContractResolver.DefaultMembersSearchFlags устарел: DefaultMembersSearchFlags устарел. Чтобы изменить сериализованное наследование членов из DefaultContractResolver и вместо этого переопределить метод GetSerializableMembers. '

Чтобы решить эту проблему, вы можете использовать следующее решение:

Создайте класс, который наследуется от DefaultContractResolver чтобы получить непубличные свойства:

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

И, наконец, используйте его следующим образом:

var contractResolver = new CustomContractResolver();
Engine r = JsonConvert.DeserializeObject<Engine>(json), new JsonSerializerSettings
            {
                ContractResolver = contractResolver
            });