c# - Reference to automatically created objects -


i attempting serialize , deserialize complex object graph:

class a contains read property containing immutable array of objects of type b. objects of type b, immutable array, created in constructor of type a.

other types contain references objects of type b obtained accessing array of object of type a.

during deserialization, need references b end pointing @ appropriate object created a constructor index, rather making brand new b objects json. i'm trying use preservereferenceshandling json.net. understandably not work, because attempts use deserialized versions of b rather a-constructed versions.

is there strategy can use here without modifying types?

edit: clarify , make extremely clear, solution must not modify type itself. can touch contract resolver, binder, reference resolver, etc. not type. also, b types cannot deserialized. must made a's constructor.

update

your question doesn't give example of trying accomplish, i'm guessing of design requirements. confirm, situation is:

  1. you have complex graph of objects serialize json.net
  2. throughout graph, there many instances of class a.
  3. a contains immutable array of instances of class b that can ever constructed inside constructor of a.
  4. each instance of a might or might not have properties serialize (not specified)
  5. each instance of b might or might not have properties serialize (not specified).
  6. also throughout graph there many references instances of b, in cases these references point instance of b inside 1 of instances of a.
  7. when deserialize graph, need references instance of b to point instance of b inside instance of a corresponding original instance, array index.
  8. you don't have code collect , discover instances of a in object graph.
  9. you cannot touch c# code classes in way, not add data contract attributes or private properties.

let's model situation following classes:

public abstract class b {     public int index { get; set; } // property modified. }  public class {     public class bactual : b     {     }      static int nextid = -1;      readonly b[] items; // private read-only array never changed.      public a()     {         items = enumerable.range(101 + 10 * interlocked.increment(ref nextid), 2).select(i => new bactual { index = }).toarray();     }      public string someproperty { get; set; }      public ienumerable<b> items     {                 {             foreach (var b in items)                 yield return b;         }     }      public string someotherproperty { get; set; } }  public class midclass {     public midclass()     {         anothera = new a();     }      public anothera { get; set; } }  public class mainclass {     public mainclass()     {         a1 = new a();         midclass = new midclass();         a2 = new a();     }      public list<b> listofb { get; set; }      public a2 { get; set; }      public midclass midclass { get; set; }      public a1 { get; set; } } 

then, serialize, need use json.net collect instances of a in object graph. next, preservereferenceshandling = preservereferenceshandling.objects set, serialize proxy class containing table of instances of a first item, root object second item.

to deserialize, preservereferenceshandling.objects must deserialize proxy class using jsonconverter a deserializes properties (if any) of a , b, , adds reference serialized "$ref" references b new instances of b allocated in constructor of a.

thus:

// used enable json.net traverse object hierarchy without writing data. public class nulljsonwriter : jsonwriter {     public nulljsonwriter()         : base()     {     }      public override void flush()     {         // nothing.     } }  public class typeinstancecollector<t> : jsonconverter t : class {     readonly list<t> instancelist = new list<t>();     readonly hashset<t> instances = new hashset<t>();      public list<t> instancelist { { return instancelist; } }      public override bool canconvert(type objecttype)     {         return typeof(t).isassignablefrom(objecttype);     }      public override bool canread { { return false; } }      public override object readjson(jsonreader reader, type objecttype, object existingvalue, jsonserializer serializer)     {         throw new notimplementedexception();     }      public override void writejson(jsonwriter writer, object value, jsonserializer serializer)     {         t instance = (t)value;         if (!instances.contains(instance))         {             instancelist.add(instance);             instances.add(instance);         }         // it's necessary write here.  null suffices.         writer.writenull();     } }  public class adeserializer : jsonconverter {     public override bool canconvert(type objecttype)     {         return typeof(a).isassignablefrom(objecttype);     }      public override bool canwrite { { return false; } }      public override object readjson(jsonreader reader, type objecttype, object existingvalue, jsonserializer serializer)     {         var obj = jobject.load(reader);         if (obj == null)             return existingvalue;         a;          var refid = (string)obj["$ref"];         if (refid != null)         {             = (a)serializer.referenceresolver.resolvereference(serializer, refid);             if (a != null)                 return a;         }          = ((a)existingvalue) ?? new a();          var items = obj["items"];         obj.remove("items");          // populate properties other items, if         // updates referenceresolver table.         using (var objreader = obj.createreader())             serializer.populate(objreader, a);          // populate properties of b items, if         if (items != null)         {             if (items.type != jtokentype.array)                 throw new jsonserializationexception("items not array");             var itemsarray = (jarray)items;             if (a.items.count() < itemsarray.count)                 throw new jsonserializationexception("too few items constructucted"); // item counts must match             foreach (var pair in a.items.zip(itemsarray, (b, o) => new { itemb = b, jobj = o }))             { #if false                 // if b class has no properties deserialize,                 var id = (string)pair.jobj["$id"];                 if (id != null)                     serializer.referenceresolver.addreference(serializer, id, pair.itemb); #else                 // if b class has properties deserialize,                 using (var objreader = pair.jobj.createreader())                 {                     // again, populate updates referenceresolver table                     serializer.populate(objreader, pair.itemb);                 } #endif             }         }          return a;     }      public override void writejson(jsonwriter writer, object value, jsonserializer serializer)     {         throw new notimplementedexception();     } }  public class rootproxy<troot, ttableitem> {     [jsonproperty("table", order = 1)]     public list<ttableitem> table { get; set; }      [jsonproperty("data", order = 2)]     public troot data { get; set; } }  public class testclass {     public static string serialize(mainclass main)     {         // first, collect instances of          var collector = new typeinstancecollector<a>();          var collectionsettings = new jsonserializersettings { preservereferenceshandling = preservereferenceshandling.objects, converters = new jsonconverter[] { collector } };         using (var jsonwriter = new nulljsonwriter())         {             jsonserializer.createdefault(collectionsettings).serialize(jsonwriter, main);         }          // serialize proxt class collected instances of @ beginning, establish reference ids instances of b.         var proxy = new rootproxy<mainclass, a> { data = main, table = collector.instancelist };         var serializationsettings = new jsonserializersettings { preservereferenceshandling = preservereferenceshandling.objects };          return jsonconvert.serializeobject(proxy, formatting.indented, serializationsettings);     }      public static mainclass deserialize(string json)     {         var serializationsettings = new jsonserializersettings { preservereferenceshandling = preservereferenceshandling.objects, converters = new jsonconverter[] { new adeserializer() } };         var proxy = jsonconvert.deserializeobject<rootproxy<mainclass, a>>(json, serializationsettings);          return proxy.data;     }      static ienumerable<a> getalla(mainclass main)     {         // testing.  in case apparently can't manually.         if (main.a1 != null)             yield return main.a1;         if (main.a2 != null)             yield return main.a2;         if (main.midclass != null && main.midclass.anothera != null)             yield return main.midclass.anothera;     }      static ienumerable<b> getallb(mainclass main)     {         return getalla(main).selectmany(a => a.items);     }      public static void test()     {         var main = new mainclass();         main.a1.someproperty = "main.a1.someproperty";         main.a1.someotherproperty = "main.a1.someotherproperty";          main.a2.someproperty = "main.a2.someproperty";         main.a2.someotherproperty = "main.a2.someotherproperty";          main.midclass.anothera.someproperty = "main.midclass.anothera.someproperty";         main.midclass.anothera.someotherproperty = "main.midclass.anothera.someotherproperty";          main.listofb = getallb(main).reverse().tolist();          var json = serialize(main);          var main2 = deserialize(json);          var json2 = serialize(main2);          foreach (var b in main2.listofb)             debug.assert(getallb(main2).contains(b)); // no assert         debug.assert(json == json2); // no assert         debug.assert(main.listofb.select(b => b.index).sequenceequal(main2.listofb.select(b => b.index))); // no assert         debug.assert(getalla(main).select(a => a.someproperty + a.someotherproperty).sequenceequal(getalla(main2).select(a => a.someproperty + a.someotherproperty))); // no assert     } } 

original answer

firstly, can use [jsonconstructor] attribute specify json.net should use non-default constructor deserialize class a. doing allow deserialize immutable collection. constructor can private, can continue create instances of b in pre-existing public constructor. note constructor argument names must match original property names.

secondly, if set preservereferenceshandling = preservereferenceshandling.objects, other objects in object graph refer directly instances of b held immutable array will, when serialized , deserialized, continue refer directly instances in deserialized immutable array. i.e., should work.

consider following test case:

public class b {     public int index { get; set; } }  public class {     static int nextid = -1;      readonly b [] items; // private read-only array never changed.      [jsonconstructor]     private a(ienumerable<b> items, string someproperty)     {         this.items = (items ?? enumerable.empty<b>()).toarray();         this.someproperty = someproperty;     }      // // create instances of "b" different properties each time default constructor called.     public a() : this(enumerable.range(101 + 10*interlocked.increment(ref nextid), 2).select(i => new b { index = }), "foobar")      {     }      public ienumerable<b> items     {                 {             foreach (var b in items)                 yield return b;         }     }      [jsonignore]     public int count { { return items.length; } }      public b getitem(int index)     {         return items[index];     }      public string someproperty { get; set; }      public string someotherproperty { get; set; } }  public class testclass {     public a { get; set; }      public list<b> listofb { get; set; }      public static void test()     {         var = new a() { someotherproperty = "something else" };         var test = new testclass { = a, listofb = a.items.reverse().tolist() };          var settings = new jsonserializersettings { preservereferenceshandling = preservereferenceshandling.objects };          var json = jsonconvert.serializeobject(test, formatting.indented, settings);         debug.writeline(json);         var test2 = jsonconvert.deserializeobject<testclass>(json, settings);          // assert pointers in "listofb" equal pointers in a.items         debug.assert(test2.listofb.all(i2 => test2.a.items.contains(i2, new referenceequalitycomparer<b>())));          // assert deserialized data same original data.         debug.assert(test2.a.someproperty == test.a.someproperty);         debug.assert(test2.a.someotherproperty == test.a.someotherproperty);         debug.assert(test2.a.items.select(i => i.index).sequenceequal(test.a.items.select(i => i.index)));          var json2 = jsonconvert.serializeobject(test2, formatting.indented, settings);         debug.writeline(json2);         debug.assert(json2 == json);     } } 

in case have created class b data, class a contains immutable collection of b creates in public constructor, , encompassing class testclass contains instance of a , list of items b taken a. when serialize this, following json:

{   "$id": "1",   "a": {     "$id": "2",     "items": [       {         "$id": "3",         "index": 101       },       {         "$id": "4",         "index": 102       }     ],     "someproperty": "foobar",     "someotherproperty": "something else"   },   "listofb": [     {       "$ref": "4"     },     {       "$ref": "3"     }   ] } 

then, when deserialize it, assert deserialized items b in listofb have pointer equality 1 of instances of b in a.items. assert deserialized properties have same values in originals, confirming non-default private constructor called deserialize immutable collection.

is want?

for checking pointer equality of instances of b, use:

public class referenceequalitycomparer<t> : iequalitycomparer<t> t : class {     #region iequalitycomparer<t> members      public bool equals(t x, t y)     {         return object.referenceequals(x, y);     }      public int gethashcode(t obj)     {         return (obj == null ? 0 : obj.gethashcode());     }      #endregion } 

Comments

Popular posts from this blog

Magento/PHP - Get phones on all members in a customer group -

php - .htaccess mod_rewrite for dynamic url which has domain names -

Website Login Issue developed in magento -