Datei: NdoJsonFormatter/NdoJsonFormatter/NdoJsonFormatter.cs

Last Commit (57b6d0d)
1 //
2 // Copyright (c) 2002-2020 Mirko Matytschak
3 // (www.netdataobjects.de)
4 //
5 // Author: Mirko Matytschak
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10 // Software, and to permit persons to whom the Software is furnished to do so, subject to the following
11 // conditions:
12
13 // The above copyright notice and this permission notice shall be included in all copies or substantial portions
14 // of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22 using System;
23 using System.Collections;
24 using System.Collections.Generic;
25 using System.IO;
26 using System.Linq;
27 using System.Reflection;
28 using System.Runtime.Serialization;
29 using System.Text;
30 using System.Threading.Tasks;
31 using NDO;
32 using NDO.ShortId;
33 using NDO.Mapping;
34 using Newtonsoft.Json;
35 using Newtonsoft.Json.Linq;
36
37 namespace NDO.JsonFormatter
38 {
39 ····/// <summary>
40 ····/// Formatter implementation which serializes NDO ObjectContainers and ChangeSetContainers into Json.
41 ····/// </summary>
42 ····public class NdoJsonFormatter : IFormatter
43 ····{
44 ········/// <inheritdoc/>
45 ········public ISurrogateSelector SurrogateSelector { get; set; }
46 ········/// <inheritdoc/>
47 ········public SerializationBinder Binder { get; set; }
48 ········/// <inheritdoc/>
49 ········public StreamingContext Context { get; set; }
50
51 ········PersistenceManager pm;
52
53 ········/// <summary>
54 ········/// Constructs an NdoJsonFormatter object. This constructor is sufficient for serialization.
55 ········/// </summary>
56 ········public NdoJsonFormatter()
57 ········{
58 ············this.pm = null;
59 ········}
60
61 ········/// <summary>
62 ········/// Constructs an NdoJsonFormatter object. This constructor should be used for deserialization.
63 ········/// </summary>
64 ········/// <param name="pm"></param>
65 ········public NdoJsonFormatter( PersistenceManager pm )
66 ········{
67 ············this.pm = pm;
68 ········}
69
70 ········IPersistenceCapable DeserializeObject( JToken jobj )
71 ········{
72 ············var shortId = (string)jobj["_oid"];
73
74 ············if (shortId != null)
75 ············{
76 ················var pc = this.pm.FindObject(shortId);
77 ················pc.NDOStateManager = null;··// Detach object
78 ················pc.FromJToken( jobj );
79 ················return pc;
80 ············}
81
82 ············return null;
83 ········}
84
85 ········IPersistenceCapable DeserializeHollowObject(JToken jobj)
86 ········{
87 ············var shortId = (string)jobj["_oid"];
88
89 ············if (shortId != null)
90 ············{
91 ················var pc = this.pm.FindObject(shortId);
92 ················pc.NDOStateManager = null;··// Detach object
93 ················return pc;
94 ············}
95
96 ············return null;
97 ········}
98
99 ········RelationChangeRecord DeserializeRelationChangeRecord(JToken jobj)
100 ········{
101 ············var parent = this.pm.FindObject((string)jobj["parent"]["_oid"]);
102 ············parent.NDOStateManager = null;··// Detach object
103 ············var child = this.pm.FindObject((string)jobj["child"]["_oid"]);
104 ············child.NDOStateManager = null;··// Detach object
105
106 ············var relationName = (string)jobj["relationName"];
107 ············var isAdded = (bool)jobj["isAdded"];
108 ············return new RelationChangeRecord(parent, child, relationName, isAdded);
109 ········}
110
111 ········void FixRelations( JToken jObj, IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
112 ········{
113 ············var t = e.GetType();
114 ············FieldMap fm = new FieldMap(t);
115 ············var mc = Metaclasses.GetClass(t);
116 ············foreach (var fi in fm.Relations)
117 ············{
118 ················var token = jObj[fi.Name];
119 ················if (token == null)
120 ····················continue;
121 ················bool isArray = typeof(IList).IsAssignableFrom( fi.FieldType );
122 ················if (token is JArray jarray)
123 ················{
124 ····················if (isArray)
125 ····················{
126 ························IList container = (IList)fi.GetValue(e);························
127 ························if (container == null)
128 ····························throw new NDOException( 20002, $"Container object of relation {t.Name}.{fi.Name} is not initialized. Please initialize the field in your class constructor." );
129
130 ························container.Clear();
131 ························foreach (var relJObj in jarray)
132 ························{
133 ····························container.Add( DeserializeObject( relJObj ) );
134 ························}
135 ····················}
136 ················}
137 ················else
138 ················{
139 ····················if (!isArray)
140 ····················{
141 ························if (token.Type == JTokenType.Null)
142 ····························fi.SetValue( e, null );
143 ························else
144 ····························fi.SetValue( e, DeserializeObject( token ) );
145 ····················}
146 ················}
147 ············}
148 ········}
149
150 ········IList DeserializeChangeSetContainer(JToken rootArray)
151 ········{
152 ············// 0: AddedObjects = new List<IPersistenceCapable>();
153 ············// 1: DeletedObjects = new List<ObjectId>();
154 ············// 2: ChangedObjects = new List<IPersistenceCapable>();
155 ············// 3: RelationChanges = new List<RelationChangeRecord>();
156
157 ············ArrayList arrayList = new ArrayList(new object[4]);
158
159 ············for (int i = 0; i < 4; i++)
160 ············{
161 ················var partialArray = rootArray[i];
162 ················if (i == 0 || i == 2)
163 ················{
164 ····················var partialList = new List<IPersistenceCapable>();
165 ····················arrayList[i] = partialList;
166 ····················foreach (var item in partialArray)
167 ····················{
168 ························partialList.Add(DeserializeObject(item));
169 ····················}
170 ················}
171 ················if (i == 1)
172 ················{
173 ····················var partialList = new List<IPersistenceCapable>();
174 ····················arrayList[i] = partialList;
175 ····················foreach (var item in partialArray)
176 ····················{
177 ························partialList.Add(DeserializeHollowObject(item));
178 ····················}
179 ················}
180 ················if (i == 3)
181 ················{
182 ····················var partialList = new List<RelationChangeRecord>();
183 ····················arrayList[i] = partialList;
184 ····················foreach (var item in partialArray)
185 ····················{
186 ························partialList.Add(DeserializeRelationChangeRecord(item));
187 ····················}
188 ················}
189 ············}
190
191 ············return arrayList;
192 ········}
193
194 ········object DeserializeRootArray( JToken rootArray )
195 ········{
196 ············var rootObjectsToken = (JArray)rootArray["rootObjects"];
197 ············var additionalObjectsToken = (JArray)rootArray["additionalObjects"];
198 ············if (rootObjectsToken.Count >= 4 && rootObjectsToken[0] is JArray)
199 ················return DeserializeChangeSetContainer(rootObjectsToken);
200
201 ············List<IPersistenceCapable> rootObjects = new List<IPersistenceCapable>();
202 ············List<IPersistenceCapable> additionalObjects = new List<IPersistenceCapable>();
203
204 ············foreach (var jObj in rootObjectsToken)
205 ············{
206 ················IPersistenceCapable e = DeserializeObject(jObj);
207 ················if (e != null)
208 ····················rootObjects.Add( e );
209 ············}
210
211 ············foreach (var jObj in additionalObjectsToken)
212 ············{
213 ················IPersistenceCapable e = DeserializeObject(jObj);
214 ················if (e != null)
215 ····················additionalObjects.Add( e );
216 ············}
217
218 ············foreach (var jObj in rootObjectsToken)
219 ············{
220 ················var shortId = (string)jObj["_oid"];
221 ················IPersistenceCapable e = rootObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
222 ················FixRelations( jObj, e, rootObjects, additionalObjects );
223 ············}
224
225 ············foreach (var jObj in additionalObjectsToken)
226 ············{
227 ················var shortId = (string)jObj["_oid"];
228 ················IPersistenceCapable e = additionalObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
229 ················FixRelations( jObj, e, rootObjects, additionalObjects );
230 ············}
231
232 ············return new ArrayList( rootObjects );
233 ········}
234
235 class Metaclasses
236 ········{
237 private static Hashtable theClasses = new Hashtable( ) ;
238
239 internal static IMetaClass GetClass( Type t )
240 ············{
241 ················if (t.IsGenericTypeDefinition)
242 ····················return null;
243
244 IMetaClass mc;
245
 
 
246 ················lock (theClasses)
247 ················{
248 if ( null == ( mc = ( IMetaClass) theClasses[t] ) )
249 ····················{
250 Type mcType = t. GetNestedType( "MetaClass", BindingFlags. NonPublic | BindingFlags. Public) ;
251 ························if (null == mcType)
252 ····························throw new NDOException( 13, "Missing nested class 'MetaClass' for type '" + t.Name + "'; the type doesn't seem to be enhanced." );
253 ························Type t2 = mcType;
254 ························if (t2.IsGenericTypeDefinition)
255 ····························t2 = t2.MakeGenericType( t.GetGenericArguments() );
256 mc = ( IMetaClass) Activator. CreateInstance( t2 ) ;
 
 
 
 
257 ························theClasses.Add( t, mc );
 
258 ····················}
259 ················}
260
261 ················return mc;
262 ············}
263 ········}
 
264
265 ········/// <summary>
266 ········/// Deserializes a Container from a stream.
267 ········/// </summary>
268 ········/// <param name="serializationStream"></param>
269 ········/// <returns></returns>
270 ········public object Deserialize( Stream serializationStream )
271 ········{
272 ············if (this.pm == null)
273 ················throw new NDOException( 20001, "PersistenceManager is not initialized. Provide a PersistenceManager in the formatter constructor." );
274
275 ············JsonSerializer serializer = new JsonSerializer();
276 ············TextReader textReader = new StreamReader( serializationStream );
277 ············var rootObject = (JToken)serializer.Deserialize(textReader, typeof(JToken));
278 ············if (rootObject == null || rootObject.Type == JTokenType.Null)
279 ················return null;
280 ············var result = DeserializeRootArray( rootObject );
281 ············this.pm.UnloadCache();
282 ············return result;
283 ········}
284
285 ········IDictionary<string, object> MakeDict( IPersistenceCapable pc )
286 ········{
287 ············var dict = pc.ToDictionary(pm);
288 ············var shortId = ((IPersistenceCapable)pc).ShortId();
289 ············var t = pc.GetType();
290 ············FieldMap fm = new FieldMap(t);
291 ············var mc = Metaclasses.GetClass(t);
292 ············foreach (var fi in fm.Relations)
293 ············{
294 ················var fiName = fi.Name;
295 ················if (( (IPersistenceCapable) pc ).NDOGetLoadState( mc.GetRelationOrdinal( fiName ) ))
296 ················{
297 ····················object relationObj = fi.GetValue(pc);
298 ····················if (relationObj is IList list)
299 ····················{
300 ························List<object> dictList = new List<object>();
301 ························foreach (IPersistenceCapable relObject in list)
302 ························{
303 ····························shortId = ( (IPersistenceCapable) relObject ).ShortId();
304 ····························dictList.Add( new { _oid = shortId } );
305 ························}
306 ························dict.Add( fiName, dictList );
307 ····················}
308 ····················else
309 ····················{
310 ························// Hollow object means, that we don't want to transfer the object to the other side.
311 ························if (relationObj == null || ((IPersistenceCapable)relationObj).NDOObjectState == NDOObjectState.Hollow)
312 ························{
313 ····························dict.Add( fiName, null );
314 ························}
315 ························else
316 ························{
317 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
318 ····························shortId = ( (IPersistenceCapable) relIPersistenceCapable ).ShortId();
319 ····························dict.Add( fiName, new { _oid = shortId } );
320 ························}
321 ····················}
322 ················}
323 ············}
324
325 ············return dict;
326 ········}
327
328 ········void InitializePm( object graph )
329 ········{
330 ············IPersistenceCapable pc;
331 ············if (graph is IList list)
332 ············{
333 ················if (list.Count == 0)
334 ····················return;
335 ················pc = (IPersistenceCapable) list[0];
336 ············}
337 ············else
338 ············{
339 ················pc = (IPersistenceCapable) graph;
340 ············}
341
342 ············this.pm = (PersistenceManager) ( pc.NDOStateManager.PersistenceManager );
343 ········}
344
345 ········void RecursivelyAddAdditionalObjects( IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
346 ········{
347 ············var t = e.GetType();
348 ············FieldMap fm = new FieldMap(t);
349 ············var mc = Metaclasses.GetClass(t);
350 ············foreach (var fi in fm.Relations)
351 ············{
352 ················if (( (IPersistenceCapable) e ).NDOGetLoadState( mc.GetRelationOrdinal( fi.Name ) ))
353 ················{
354 ····················object relationObj = fi.GetValue(e);
355 ····················if (relationObj is IList list)
356 ····················{
357 ························List<object> dictList = new List<object>();
358 ························foreach (IPersistenceCapable relIPersistenceCapable in list)
359 ························{
360 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
361 ····························{
362 ································additionalObjects.Add( relIPersistenceCapable );
363 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
364 ····························}
365 ························}
366 ····················}
367 ····················else
368 ····················{
369 ························// Hollow object means, that we don't want to transfer the object to the other side.
370 ························if (relationObj != null && ((IPersistenceCapable)relationObj).NDOObjectState != NDOObjectState.Hollow)
371 ························{
372 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
373 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
374 ····························{
375 ································additionalObjects.Add( relIPersistenceCapable );
376 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
377 ····························}
378 ························}
379 ····················}
380 ················}
381 ············}
382 ········}
383
384 ········object MapRelationChangeRecord(RelationChangeRecord rcr)
385 ········{
386 ············return new
387 ············{
388 ················parent = new { _oid = rcr.Parent.NDOObjectId.ToShortId() },
389 ················child··= new { _oid = rcr.Child.NDOObjectId.ToShortId() },
390 ················isAdded = rcr.IsAdded,
391 ················relationName = rcr.RelationName,
392 ················_oid = "RelationChangeRecord"
393 ············};
394 ········}
395
396 ········void SerializeChangeSet(Stream serializationStream, IList graph)
397 ········{
398 ············// A ChangeSetContainer consists of 4 lists of deleted, added, changed objects, and the relation changes.
399 ············// AddedObjects = new List<IPersistenceCapable>();
400 ············// DeletedObjects = new List<ObjectId>();
401 ············// ChangedObjects = new List<IPersistenceCapable>();
402 ············// RelationChanges = new List<RelationChangeRecord>();
403 ············List<List<object>> resultObjects = new List<List<object>>(graph.Count);
404 ············foreach (IList list in graph)
405 ············{
406 ················List<object> partialResult = new List<object>();
407 ················resultObjects.Add(partialResult);
408 ················foreach (var item in list)
409 ················{
410 ····················if (item is IPersistenceCapable pc)
411 ························partialResult.Add(MakeDict(pc));
412 ····················else if (item is ObjectId oid)
413 ························partialResult.Add(new { _oid = oid.ToShortId() });
414 ····················else if (item is RelationChangeRecord rcr)
415 ························partialResult.Add(MapRelationChangeRecord(rcr));
416 ····················else
417 ························throw new NDOException(20003, $"NDOJsonFormatter: unknown element of type {item.GetType().FullName} in ChangeSetContainer.");
418 ················}
419 ············}
420
421 ············var json = JsonConvert.SerializeObject(new { rootObjects = resultObjects, additionalObjects = new object[] { } });
422 ············var byteArray = Encoding.UTF8.GetBytes(json);
423 ············serializationStream.Write(byteArray, 0, byteArray.Length);
424 ········}
425
426 ········/// <summary>
427 ········/// Serializes an object graph to a stream.
428 ········/// </summary>
429 ········/// <param name="serializationStream"></param>
430 ········/// <param name="graph"></param>
431 ········public void Serialize( Stream serializationStream, object graph )
432 ········{
433 ············string json = null;
434 ············if (this.pm == null)
435 ················InitializePm( graph );
436 ············List<object> rootObjects = new List<object>();
437 ············List<object> additionalObjects = new List<object>();
438 ············List<IPersistenceCapable> rootObjectList = new List<IPersistenceCapable>();
439 ············List<IPersistenceCapable> additionalObjectList = new List<IPersistenceCapable>();
440
441 ············IList list = graph as IList;
442 ············if (list != null)
443 ············{
444 ················if (list.Count > 0 && list[0] is IList)
445 ················{
446 ····················// Change Set
447 ····················SerializeChangeSet(serializationStream, list);
448 ····················return;
449 ················}
450 ················foreach (IPersistenceCapable e in list)
451 ················{
452 ····················rootObjectList.Add( e );
453 ················}
454 ············}
455 ············else if (graph is IPersistenceCapable e)
456 ············{
457 ················rootObjectList.Add( e );
458 ············}
459
460 ············foreach (var e in rootObjectList)
461 ············{
462 ················RecursivelyAddAdditionalObjects( e, rootObjectList, additionalObjectList );
463 ············}
464
465 ············foreach (var e in rootObjectList)
466 ············{
467 ················rootObjects.Add( MakeDict( e ) );
468 ············}
469
470 ············foreach (var e in additionalObjectList)
471 ············{
472 ················additionalObjects.Add( MakeDict( e ) );
473 ············}
474
475 ············json = JsonConvert.SerializeObject( new { rootObjects, additionalObjects } );
476 ············var byteArray = Encoding.UTF8.GetBytes(json);
477 ············serializationStream.Write( byteArray, 0, byteArray.Length );
478 ········}
479 ····}
480 }
481
New Commit (4a7e8ab)
1 //
2 // Copyright (c) 2002-2020 Mirko Matytschak
3 // (www.netdataobjects.de)
4 //
5 // Author: Mirko Matytschak
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8 // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
10 // Software, and to permit persons to whom the Software is furnished to do so, subject to the following
11 // conditions:
12
13 // The above copyright notice and this permission notice shall be included in all copies or substantial portions
14 // of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19 // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22 using System;
23 using System.Collections;
24 using System.Collections.Generic;
25 using System.IO;
26 using System.Linq;
27 using System.Reflection;
28 using System.Runtime.Serialization;
29 using System.Text;
30 using System.Threading.Tasks;
31 using NDO;
32 using NDO.ShortId;
33 using NDO.Mapping;
34 using Newtonsoft.Json;
35 using Newtonsoft.Json.Linq;
36
37 namespace NDO.JsonFormatter
38 {
39 ····/// <summary>
40 ····/// Formatter implementation which serializes NDO ObjectContainers and ChangeSetContainers into Json.
41 ····/// </summary>
42 ····public class NdoJsonFormatter : IFormatter
43 ····{
44 ········/// <inheritdoc/>
45 ········public ISurrogateSelector SurrogateSelector { get; set; }
46 ········/// <inheritdoc/>
47 ········public SerializationBinder Binder { get; set; }
48 ········/// <inheritdoc/>
49 ········public StreamingContext Context { get; set; }
50
51 ········PersistenceManager pm;
52
53 ········/// <summary>
54 ········/// Constructs an NdoJsonFormatter object. This constructor is sufficient for serialization.
55 ········/// </summary>
56 ········public NdoJsonFormatter()
57 ········{
58 ············this.pm = null;
59 ········}
60
61 ········/// <summary>
62 ········/// Constructs an NdoJsonFormatter object. This constructor should be used for deserialization.
63 ········/// </summary>
64 ········/// <param name="pm"></param>
65 ········public NdoJsonFormatter( PersistenceManager pm )
66 ········{
67 ············this.pm = pm;
68 ········}
69
70 ········IPersistenceCapable DeserializeObject( JToken jobj )
71 ········{
72 ············var shortId = (string)jobj["_oid"];
73
74 ············if (shortId != null)
75 ············{
76 ················var pc = this.pm.FindObject(shortId);
77 ················pc.NDOStateManager = null;··// Detach object
78 ················pc.FromJToken( jobj );
79 ················return pc;
80 ············}
81
82 ············return null;
83 ········}
84
85 ········IPersistenceCapable DeserializeHollowObject(JToken jobj)
86 ········{
87 ············var shortId = (string)jobj["_oid"];
88
89 ············if (shortId != null)
90 ············{
91 ················var pc = this.pm.FindObject(shortId);
92 ················pc.NDOStateManager = null;··// Detach object
93 ················return pc;
94 ············}
95
96 ············return null;
97 ········}
98
99 ········RelationChangeRecord DeserializeRelationChangeRecord(JToken jobj)
100 ········{
101 ············var parent = this.pm.FindObject((string)jobj["parent"]["_oid"]);
102 ············parent.NDOStateManager = null;··// Detach object
103 ············var child = this.pm.FindObject((string)jobj["child"]["_oid"]);
104 ············child.NDOStateManager = null;··// Detach object
105
106 ············var relationName = (string)jobj["relationName"];
107 ············var isAdded = (bool)jobj["isAdded"];
108 ············return new RelationChangeRecord(parent, child, relationName, isAdded);
109 ········}
110
111 ········void FixRelations( JToken jObj, IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
112 ········{
113 ············var t = e.GetType();
114 ············FieldMap fm = new FieldMap(t);
115 ············var mc = Metaclasses.GetClass(t);
116 ············foreach (var fi in fm.Relations)
117 ············{
118 ················var token = jObj[fi.Name];
119 ················if (token == null)
120 ····················continue;
121 ················bool isArray = typeof(IList).IsAssignableFrom( fi.FieldType );
122 ················if (token is JArray jarray)
123 ················{
124 ····················if (isArray)
125 ····················{
126 ························IList container = (IList)fi.GetValue(e);························
127 ························if (container == null)
128 ····························throw new NDOException( 20002, $"Container object of relation {t.Name}.{fi.Name} is not initialized. Please initialize the field in your class constructor." );
129
130 ························container.Clear();
131 ························foreach (var relJObj in jarray)
132 ························{
133 ····························container.Add( DeserializeObject( relJObj ) );
134 ························}
135 ····················}
136 ················}
137 ················else
138 ················{
139 ····················if (!isArray)
140 ····················{
141 ························if (token.Type == JTokenType.Null)
142 ····························fi.SetValue( e, null );
143 ························else
144 ····························fi.SetValue( e, DeserializeObject( token ) );
145 ····················}
146 ················}
147 ············}
148 ········}
149
150 ········IList DeserializeChangeSetContainer(JToken rootArray)
151 ········{
152 ············// 0: AddedObjects = new List<IPersistenceCapable>();
153 ············// 1: DeletedObjects = new List<ObjectId>();
154 ············// 2: ChangedObjects = new List<IPersistenceCapable>();
155 ············// 3: RelationChanges = new List<RelationChangeRecord>();
156
157 ············ArrayList arrayList = new ArrayList(new object[4]);
158
159 ············for (int i = 0; i < 4; i++)
160 ············{
161 ················var partialArray = rootArray[i];
162 ················if (i == 0 || i == 2)
163 ················{
164 ····················var partialList = new List<IPersistenceCapable>();
165 ····················arrayList[i] = partialList;
166 ····················foreach (var item in partialArray)
167 ····················{
168 ························partialList.Add(DeserializeObject(item));
169 ····················}
170 ················}
171 ················if (i == 1)
172 ················{
173 ····················var partialList = new List<IPersistenceCapable>();
174 ····················arrayList[i] = partialList;
175 ····················foreach (var item in partialArray)
176 ····················{
177 ························partialList.Add(DeserializeHollowObject(item));
178 ····················}
179 ················}
180 ················if (i == 3)
181 ················{
182 ····················var partialList = new List<RelationChangeRecord>();
183 ····················arrayList[i] = partialList;
184 ····················foreach (var item in partialArray)
185 ····················{
186 ························partialList.Add(DeserializeRelationChangeRecord(item));
187 ····················}
188 ················}
189 ············}
190
191 ············return arrayList;
192 ········}
193
194 ········object DeserializeRootArray( JToken rootArray )
195 ········{
196 ············var rootObjectsToken = (JArray)rootArray["rootObjects"];
197 ············var additionalObjectsToken = (JArray)rootArray["additionalObjects"];
198 ············if (rootObjectsToken.Count >= 4 && rootObjectsToken[0] is JArray)
199 ················return DeserializeChangeSetContainer(rootObjectsToken);
200
201 ············List<IPersistenceCapable> rootObjects = new List<IPersistenceCapable>();
202 ············List<IPersistenceCapable> additionalObjects = new List<IPersistenceCapable>();
203
204 ············foreach (var jObj in rootObjectsToken)
205 ············{
206 ················IPersistenceCapable e = DeserializeObject(jObj);
207 ················if (e != null)
208 ····················rootObjects.Add( e );
209 ············}
210
211 ············foreach (var jObj in additionalObjectsToken)
212 ············{
213 ················IPersistenceCapable e = DeserializeObject(jObj);
214 ················if (e != null)
215 ····················additionalObjects.Add( e );
216 ············}
217
218 ············foreach (var jObj in rootObjectsToken)
219 ············{
220 ················var shortId = (string)jObj["_oid"];
221 ················IPersistenceCapable e = rootObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
222 ················FixRelations( jObj, e, rootObjects, additionalObjects );
223 ············}
224
225 ············foreach (var jObj in additionalObjectsToken)
226 ············{
227 ················var shortId = (string)jObj["_oid"];
228 ················IPersistenceCapable e = additionalObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
229 ················FixRelations( jObj, e, rootObjects, additionalObjects );
230 ············}
231
232 ············return new ArrayList( rootObjects );
233 ········}
234
235 internal class Metaclasses
236 ········{
237 private static Dictionary<Type, IMetaClass2> theClasses = new Dictionary<Type, IMetaClass2>( ) ;
238
239 internal static IMetaClass2 GetClass( Type t )
240 ············{
241 ················if (t.IsGenericTypeDefinition)
242 ····················return null;
243
244 IMetaClass2 mc;
245
246 ················if (!theClasses.TryGetValue( t, out mc ))
247 ················{
248 ····················lock (theClasses)
249 ····················{
250 if ( !theClasses. TryGetValue( t, out mc ) ) // Threading double check
251 ························{
252 Type mcType = t. GetNestedType( "MetaClass", BindingFlags. NonPublic | BindingFlags. Public ) ;
253 ····························if (null == mcType)
254 ································throw new NDOException( 13, "Missing nested class 'MetaClass' for type '" + t.Name + "'; the type doesn't seem to be enhanced." );
255 ····························Type t2 = mcType;
256 ····························if (t2.IsGenericTypeDefinition)
257 ································t2 = t2.MakeGenericType( t.GetGenericArguments() );
258 var o = Activator. CreateInstance( t2, t ) ;
259 ····························if (o is IMetaClass2 mc2)
260 ································mc = mc2;
261 ····························else
262 ································throw new NDOException( 101010, $"MetaClass for type '{t.FullName}' must implement IMetaClass2, but doesn't. Recompile the assembly with NDO v. >= 4.0.9" );
263 ····························theClasses.Add( t, mc );
264 ························}
265 ····················}
266 ················}
267
268 ················return mc;
269 ············}
270 ········}
271
272
273 ········/// <summary>
274 ········/// Deserializes a Container from a stream.
275 ········/// </summary>
276 ········/// <param name="serializationStream"></param>
277 ········/// <returns></returns>
278 ········public object Deserialize( Stream serializationStream )
279 ········{
280 ············if (this.pm == null)
281 ················throw new NDOException( 20001, "PersistenceManager is not initialized. Provide a PersistenceManager in the formatter constructor." );
282
283 ············JsonSerializer serializer = new JsonSerializer();
284 ············TextReader textReader = new StreamReader( serializationStream );
285 ············var rootObject = (JToken)serializer.Deserialize(textReader, typeof(JToken));
286 ············if (rootObject == null || rootObject.Type == JTokenType.Null)
287 ················return null;
288 ············var result = DeserializeRootArray( rootObject );
289 ············this.pm.UnloadCache();
290 ············return result;
291 ········}
292
293 ········IDictionary<string, object> MakeDict( IPersistenceCapable pc )
294 ········{
295 ············var dict = pc.ToDictionary(pm);
296 ············var shortId = ((IPersistenceCapable)pc).ShortId();
297 ············var t = pc.GetType();
298 ············FieldMap fm = new FieldMap(t);
299 ············var mc = Metaclasses.GetClass(t);
300 ············foreach (var fi in fm.Relations)
301 ············{
302 ················var fiName = fi.Name;
303 ················if (( (IPersistenceCapable) pc ).NDOGetLoadState( mc.GetRelationOrdinal( fiName ) ))
304 ················{
305 ····················object relationObj = fi.GetValue(pc);
306 ····················if (relationObj is IList list)
307 ····················{
308 ························List<object> dictList = new List<object>();
309 ························foreach (IPersistenceCapable relObject in list)
310 ························{
311 ····························shortId = ( (IPersistenceCapable) relObject ).ShortId();
312 ····························dictList.Add( new { _oid = shortId } );
313 ························}
314 ························dict.Add( fiName, dictList );
315 ····················}
316 ····················else
317 ····················{
318 ························// Hollow object means, that we don't want to transfer the object to the other side.
319 ························if (relationObj == null || ((IPersistenceCapable)relationObj).NDOObjectState == NDOObjectState.Hollow)
320 ························{
321 ····························dict.Add( fiName, null );
322 ························}
323 ························else
324 ························{
325 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
326 ····························shortId = ( (IPersistenceCapable) relIPersistenceCapable ).ShortId();
327 ····························dict.Add( fiName, new { _oid = shortId } );
328 ························}
329 ····················}
330 ················}
331 ············}
332
333 ············return dict;
334 ········}
335
336 ········void InitializePm( object graph )
337 ········{
338 ············IPersistenceCapable pc;
339 ············if (graph is IList list)
340 ············{
341 ················if (list.Count == 0)
342 ····················return;
343 ················pc = (IPersistenceCapable) list[0];
344 ············}
345 ············else
346 ············{
347 ················pc = (IPersistenceCapable) graph;
348 ············}
349
350 ············this.pm = (PersistenceManager) ( pc.NDOStateManager.PersistenceManager );
351 ········}
352
353 ········void RecursivelyAddAdditionalObjects( IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
354 ········{
355 ············var t = e.GetType();
356 ············FieldMap fm = new FieldMap(t);
357 ············var mc = Metaclasses.GetClass(t);
358 ············foreach (var fi in fm.Relations)
359 ············{
360 ················if (( (IPersistenceCapable) e ).NDOGetLoadState( mc.GetRelationOrdinal( fi.Name ) ))
361 ················{
362 ····················object relationObj = fi.GetValue(e);
363 ····················if (relationObj is IList list)
364 ····················{
365 ························List<object> dictList = new List<object>();
366 ························foreach (IPersistenceCapable relIPersistenceCapable in list)
367 ························{
368 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
369 ····························{
370 ································additionalObjects.Add( relIPersistenceCapable );
371 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
372 ····························}
373 ························}
374 ····················}
375 ····················else
376 ····················{
377 ························// Hollow object means, that we don't want to transfer the object to the other side.
378 ························if (relationObj != null && ((IPersistenceCapable)relationObj).NDOObjectState != NDOObjectState.Hollow)
379 ························{
380 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
381 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
382 ····························{
383 ································additionalObjects.Add( relIPersistenceCapable );
384 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
385 ····························}
386 ························}
387 ····················}
388 ················}
389 ············}
390 ········}
391
392 ········object MapRelationChangeRecord(RelationChangeRecord rcr)
393 ········{
394 ············return new
395 ············{
396 ················parent = new { _oid = rcr.Parent.NDOObjectId.ToShortId() },
397 ················child··= new { _oid = rcr.Child.NDOObjectId.ToShortId() },
398 ················isAdded = rcr.IsAdded,
399 ················relationName = rcr.RelationName,
400 ················_oid = "RelationChangeRecord"
401 ············};
402 ········}
403
404 ········void SerializeChangeSet(Stream serializationStream, IList graph)
405 ········{
406 ············// A ChangeSetContainer consists of 4 lists of deleted, added, changed objects, and the relation changes.
407 ············// AddedObjects = new List<IPersistenceCapable>();
408 ············// DeletedObjects = new List<ObjectId>();
409 ············// ChangedObjects = new List<IPersistenceCapable>();
410 ············// RelationChanges = new List<RelationChangeRecord>();
411 ············List<List<object>> resultObjects = new List<List<object>>(graph.Count);
412 ············foreach (IList list in graph)
413 ············{
414 ················List<object> partialResult = new List<object>();
415 ················resultObjects.Add(partialResult);
416 ················foreach (var item in list)
417 ················{
418 ····················if (item is IPersistenceCapable pc)
419 ························partialResult.Add(MakeDict(pc));
420 ····················else if (item is ObjectId oid)
421 ························partialResult.Add(new { _oid = oid.ToShortId() });
422 ····················else if (item is RelationChangeRecord rcr)
423 ························partialResult.Add(MapRelationChangeRecord(rcr));
424 ····················else
425 ························throw new NDOException(20003, $"NDOJsonFormatter: unknown element of type {item.GetType().FullName} in ChangeSetContainer.");
426 ················}
427 ············}
428
429 ············var json = JsonConvert.SerializeObject(new { rootObjects = resultObjects, additionalObjects = new object[] { } });
430 ············var byteArray = Encoding.UTF8.GetBytes(json);
431 ············serializationStream.Write(byteArray, 0, byteArray.Length);
432 ········}
433
434 ········/// <summary>
435 ········/// Serializes an object graph to a stream.
436 ········/// </summary>
437 ········/// <param name="serializationStream"></param>
438 ········/// <param name="graph"></param>
439 ········public void Serialize( Stream serializationStream, object graph )
440 ········{
441 ············string json = null;
442 ············if (this.pm == null)
443 ················InitializePm( graph );
444 ············List<object> rootObjects = new List<object>();
445 ············List<object> additionalObjects = new List<object>();
446 ············List<IPersistenceCapable> rootObjectList = new List<IPersistenceCapable>();
447 ············List<IPersistenceCapable> additionalObjectList = new List<IPersistenceCapable>();
448
449 ············IList list = graph as IList;
450 ············if (list != null)
451 ············{
452 ················if (list.Count > 0 && list[0] is IList)
453 ················{
454 ····················// Change Set
455 ····················SerializeChangeSet(serializationStream, list);
456 ····················return;
457 ················}
458 ················foreach (IPersistenceCapable e in list)
459 ················{
460 ····················rootObjectList.Add( e );
461 ················}
462 ············}
463 ············else if (graph is IPersistenceCapable e)
464 ············{
465 ················rootObjectList.Add( e );
466 ············}
467
468 ············foreach (var e in rootObjectList)
469 ············{
470 ················RecursivelyAddAdditionalObjects( e, rootObjectList, additionalObjectList );
471 ············}
472
473 ············foreach (var e in rootObjectList)
474 ············{
475 ················rootObjects.Add( MakeDict( e ) );
476 ············}
477
478 ············foreach (var e in additionalObjectList)
479 ············{
480 ················additionalObjects.Add( MakeDict( e ) );
481 ············}
482
483 ············json = JsonConvert.SerializeObject( new { rootObjects, additionalObjects } );
484 ············var byteArray = Encoding.UTF8.GetBytes(json);
485 ············serializationStream.Write( byteArray, 0, byteArray.Length );
486 ········}
487 ····}
488 }
489