6

実際のコードの簡単な例があります。JSON にシリアル化しTestClass、から派生したクラス のオブジェクトを逆シリアル化する必要がありますLetters。どちらのクラスにもパラメーターを持つコンストラクターがあります。

    public class TestClass : Letters
    {
        public string[] Names { get; private set; }

        public TestClass(string[] names)
            : base(names)
           // : base(new [] { "A", "B", })
           // : base(names.Select(a => a.Substring(0, 1)).ToArray())
        {
            Names = names;
        }
    }

    public abstract class Letters
    {
        public string[] FirstLetters { get; private set; }

        protected Letters(string[] letters)
        {
            FirstLetters = letters;
        }
    }

のオブジェクトはTestClass有効な JSON にシリアル化されていますが、逆シリアル化してオブジェクトに戻そうとすると、メッセージCollection was of a fixed sizeでNotSupportedExceptionがスローされます。

これが私のテストです

    [Fact]
    public void JsonNamesTest()
    {
        var expected = new TestClass(new [] { "Alex", "Peter", "John", });

        var serialized = JsonConvert.SerializeObject(expected);
        Console.WriteLine(serialized);

        Assert.False(string.IsNullOrWhiteSpace(serialized));

        var actual = JsonConvert.DeserializeObject<TestClass>(serialized);

        AssertEx.PrimitivePropertiesEqual(expected, actual);
    }
4

2 に答える 2

6

Json.Net では、すべてのクラスを逆シリアル化するために、パラメーターなしのコンストラクターが必要です。そうしないと、コンストラクターを呼び出す方法がわかりません。クラスを変更せずにこれを回避する 1 つの方法はJsonConverter、JSON からオブジェクト インスタンスを作成するカスタムを作成することです。例えば:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        string[] names = jo["Names"].ToObject<string[]>();
        return new TestClass(names);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

次に、次のようにクラスを逆シリアル化すると、動作するはずです。

var actual = JsonConvert.DeserializeObject<TestClass>(serialized, new TestClassConverter());
于 2013-10-22T08:33:53.820 に答える
1

ありがとう、うまくいきます!私の例では、より一般的な使用法のためにコードを変更しました。

私は考えます

  • 1つのパブリックコンストラクターのみです
  • シリアライズされたパラメーターはコンストラクターのパラメーターと同じ名前です (大文字と小文字は区別されません)

    public class ParametersContructorConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(Letters).IsAssignableFrom(objectType);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var jo = JObject.Load(reader);
            var contructor = objectType.GetConstructors().FirstOrDefault();
    
            if (contructor == null)
            {
                return serializer.Deserialize(reader);
            }
    
            var parameters = contructor.GetParameters();
            var values = parameters.Select(p => jo.GetValue(p.Name, StringComparison.InvariantCultureIgnoreCase).ToObject(p.ParameterType)).ToArray();
    
            return contructor.Invoke(values);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    }
    
于 2013-10-22T09:24:52.070 に答える