2017-02-23 63 views
1

這是this one的後續問題。我試圖實現的是將我的類替換爲[JsonConstructor],以便在沒有進行任何序列化的項目中擺脫對Json.Net的依賴關係。通過我從其他來源收集的信息,使用ContractResolver是實現某種「爲Foo類型使用代理欄創建實例」的示例(請參閱MongoDB's class map)。對我來說重要的是,我想堅持我的實體中只讀字段,因此我必須使用一些帶參數的工廠方法來創建實例,而在創建過程中將給定參數分配給只讀字段。 (我從一個公共無參數構造函數,一個屬性構造函數,一個私有無參數構造函數,到根本沒有歸屬構造函數,並且每一步都探索了被覆蓋的行爲我的合同分解的方法):使用ContractResolver在Newtonsoft的Json.NET中實現等效於MongoDB的BSON類映射

class Thing 
{ 
    public int Id { get; private set; } 
    public string Name { get; private set; } 

    //private Thing() { } 

    //[JsonConstructor] 
    private Thing(int id, string name) 
    { 
    this.Id = id; 
    this.Name = name; 
    } 

    public static Thing Create(int id, string name) 
    { 
    return new Thing(id, name); 
    } 

    public static object Create2(object[] values) 
    { 
    return new Thing((int)values[0], (string)values[1]); 
    } 
} 

我的合同解析如下所示:

class CustomResolver : DefaultContractResolver 
{ 
    public override JsonContract ResolveContract(Type type) 
    { 
    var b = base.ResolveContract(type); 

    if(type == typeof(Thing)) 
    { 
     var bb = b as JsonObjectContract; 

     // figured out the name of the backing field using ILSpy 
     var prop = typeof(JsonObjectContract).GetField("_creatorParameters", 
     System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 

     // in order to set the value of property CreatorParameters 
     // which is used as parameter when OverrideCreator is invoked 
     prop.SetValue(bb, bb.Properties); 

     bb.OverrideCreator = new ObjectConstructor<object>(Thing.Create2); 
    } 

    return b; 
    } 
} 

這是它的用法:

Thing t1 = Thing.Create("Flim", 1); 

// Serializer settings 
JsonSerializerSettings settings = new JsonSerializerSettings(); 
settings.ContractResolver = new CustomResolver(); 
settings.PreserveReferencesHandling = PreserveReferencesHandling.None; 
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; 
settings.Formatting = Newtonsoft.Json.Formatting.Indented; 

string json = JsonConvert.SerializeObject(t1, settings); 
Thing t2 = JsonConvert.DeserializeObject<Thing>(json, settings); 

它可以像我期望的那樣工作,但是由於我使用反射破解了系統,我相信我正在做一些完全錯誤的事情,並且必須有一種更清晰的方式。有任何想法嗎?

編輯

什麼我都沒有想到直到現在,我有一些類這些都歸功於與[JsonConverter(typeof(SomeSubstituteType))]這也是我就會改掉,如果我想完全elmininate的depency的。這些類迄今尚未包含在合同解析器類的類型檢查中。有沒有一種優雅的方式可以讓它在沒有屬性的情況下工作,也不需要通過以某種方式將JsonSerializerSettings另外配置到目前爲止配置的內容來在解析器中添加額外的檢查?

回答

2

這是真的,DefaultContractResolver目前沒有可以很容易地注入邏輯來選擇一個自定義的構造函數,因爲:

不過你可以進行以下改進代碼:

  1. 覆蓋CreateObjectContract而非ResolveContract。前者被稱爲第一次遇到給定類型類型的對象來構造隨後被緩存的類型的契約。後者被稱爲遇到的每個對象,包括基元。因此,覆蓋前者將更具性能。此外,CreateObjectContract()處於線程安全鎖定範圍內,而ResolveContract()不是,可以同時從多個線程中調用。因此你當前的代碼可能不是線程安全的。

  2. JsonObjectContract.CreatorParameters是一個可變的集合,所以你可以清除內容並添加新的屬性。

因此,你的合同解析器可能看起來像:

class CustomResolver : DefaultContractResolver 
{ 
    protected override JsonObjectContract CreateObjectContract(Type objectType) 
    { 
     var contract = base.CreateObjectContract(objectType); 

     if (objectType == typeof(Thing)) 
     { 
      contract.CreatorParameters.Clear(); 
      // For safety, specify the order concretely. 
      contract.CreatorParameters.AddProperty(contract.Properties["Id"]); // Use nameof() in latest version. 
      contract.CreatorParameters.AddProperty(contract.Properties["Name"]); // Use nameof() in latest version. 
      contract.OverrideCreator = new ObjectConstructor<object>(Thing.Create2); 
     } 

     return contract; 
    } 
} 

fiddle

更新

我有些班級這些都歸功於與[JsonConverter(typeof(SomeSubstituteType))]這也是我就會改掉,如果我想完全elmininate的depency的...有一種優雅的方式來度過,即使沒有工作屬性,並且不需要在解析器中添加額外的檢查,通過配置JsonSerializerSettings以外的配置到目前爲止?

是,確保JsonConverter.CanConvert()爲您的轉換器來實現,然後將它們添加到JsonSerializerSettings.Converters

public class SomeSubstituteType : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(SomeOriginalType).IsAssignableFrom(objectType); 
    } 

    // Remainder as before 
} 

然後執行:

var settings = new JsonSerializerSettings 
{ 
    Converters = { new SomeSubstituteType(), new SecondConverter(), ... }, 
}; 
+0

請看看我的編輯。或者我可以做另一個後續問題。 – David

+0

@David - 答案已更新。 – dbc