Datei: NdoJsonFormatter/NdoJsonFormatter/NdoJsonFormatter.cs

Last Commit (1814d4b)
1 //
2 // Copyright (c) 2002-2024 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.Text;
29 using NDO.ShortId;
30 using NDO.Mapping;
31 using Newtonsoft.Json;
32 using Newtonsoft.Json.Linq;
33 using NDOInterfaces;
34
35 namespace NDO.JsonFormatter
36 {
37 ····/// <summary>
38 ····/// Formatter implementation which serializes NDO ObjectContainers and ChangeSetContainers into Json.
39 ····/// </summary>
40 ····public class NdoJsonFormatter : INdoFormatter
41 ····{
42 ········PersistenceManager pm;
43
44 ········/// <summary>
45 ········/// Constructs an NdoJsonFormatter object. This constructor is sufficient for serialization.
46 ········/// </summary>
47 ········public NdoJsonFormatter()
48 ········{
49 ············this.pm = null;
50 ········}
51
52 ········/// <summary>
53 ········/// Constructs an NdoJsonFormatter object. This constructor should be used for deserialization.
54 ········/// </summary>
55 ········/// <param name="pm"></param>
56 ········public NdoJsonFormatter( PersistenceManager pm )
57 ········{
58 ············this.pm = pm;
59 ········}
60
61 ········IPersistenceCapable DeserializeObject( JToken jobj )
62 ········{
63 ············var shortId = (string)jobj["_oid"];
64
65 ············if (shortId != null)
66 ············{
67 ················var pc = this.pm.FindObject(shortId);
68 ················pc.NDOStateManager = null;··// Detach object
69 ················pc.FromJToken( jobj );
70 ················return pc;
71 ············}
72
73 ············return null;
74 ········}
75
76 ········IPersistenceCapable DeserializeHollowObject(JToken jobj)
77 ········{
78 ············var shortId = (string)jobj["_oid"];
79
80 ············if (shortId != null)
81 ············{
82 ················var pc = this.pm.FindObject(shortId);
83 ················pc.NDOStateManager = null;··// Detach object
84 ················return pc;
85 ············}
86
87 ············return null;
88 ········}
89
90 ········RelationChangeRecord DeserializeRelationChangeRecord(JToken jobj)
91 ········{
92 ············var parent = this.pm.FindObject((string)jobj["parent"]["_oid"]);
93 ············parent.NDOStateManager = null;··// Detach object
94 ············var child = this.pm.FindObject((string)jobj["child"]["_oid"]);
95 ············child.NDOStateManager = null;··// Detach object
96
97 ············var relationName = (string)jobj["relationName"];
98 ············var isAdded = (bool)jobj["isAdded"];
99 ············return new RelationChangeRecord(parent, child, relationName, isAdded);
100 ········}
101
102 ········void FixRelations( JToken jObj, IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
103 ········{
104 ············var t = e.GetType();
105 ············FieldMap fm = new FieldMap(t);
106 var mc = Metaclasses. GetClass( t) ;
107 ············foreach (var fi in fm.Relations)
108 ············{
109 ················var token = jObj[fi.Name];
110 ················if (token == null)
111 ····················continue;
112 ················bool isArray = typeof(IList).IsAssignableFrom( fi.FieldType );
113 ················if (token is JArray jarray)
114 ················{
115 ····················if (isArray)
116 ····················{
117 ························IList container = (IList)fi.GetValue(e);························
118 ························if (container == null)
119 ····························throw new NDOException( 20002, $"Container object of relation {t.Name}.{fi.Name} is not initialized. Please initialize the field in your class constructor." );
120
121 ························container.Clear();
122 ························foreach (var relJObj in jarray)
123 ························{
124 ····························container.Add( DeserializeObject( relJObj ) );
125 ························}
126 ····················}
127 ················}
128 ················else
129 ················{
130 ····················if (!isArray)
131 ····················{
132 ························if (token.Type == JTokenType.Null)
133 ····························fi.SetValue( e, null );
134 ························else
135 ····························fi.SetValue( e, DeserializeObject( token ) );
136 ····················}
137 ················}
138 ············}
139 ········}
140
141 ········IList DeserializeChangeSetContainer(JToken rootArray)
142 ········{
143 ············// 0: AddedObjects = new List<IPersistenceCapable>();
144 ············// 1: DeletedObjects = new List<ObjectId>();
145 ············// 2: ChangedObjects = new List<IPersistenceCapable>();
146 ············// 3: RelationChanges = new List<RelationChangeRecord>();
147
148 ············ArrayList arrayList = new ArrayList(new object[4]);
149
150 ············for (int i = 0; i < 4; i++)
151 ············{
152 ················var partialArray = rootArray[i];
153 ················if (i == 0 || i == 2)
154 ················{
155 ····················var partialList = new List<IPersistenceCapable>();
156 ····················arrayList[i] = partialList;
157 ····················foreach (var item in partialArray)
158 ····················{
159 ························partialList.Add(DeserializeObject(item));
160 ····················}
161 ················}
162 ················if (i == 1)
163 ················{
164 ····················var partialList = new List<IPersistenceCapable>();
165 ····················arrayList[i] = partialList;
166 ····················foreach (var item in partialArray)
167 ····················{
168 ························partialList.Add(DeserializeHollowObject(item));
169 ····················}
170 ················}
171 ················if (i == 3)
172 ················{
173 ····················var partialList = new List<RelationChangeRecord>();
174 ····················arrayList[i] = partialList;
175 ····················foreach (var item in partialArray)
176 ····················{
177 ························partialList.Add(DeserializeRelationChangeRecord(item));
178 ····················}
179 ················}
180 ············}
181
182 ············return arrayList;
183 ········}
184
185 ········object DeserializeRootArray( JToken rootArray )
186 ········{
187 ············var rootObjectsToken = (JArray)rootArray["rootObjects"];
188 ············var additionalObjectsToken = (JArray)rootArray["additionalObjects"];
189 ············if (rootObjectsToken.Count >= 4 && rootObjectsToken[0] is JArray)
190 ················return DeserializeChangeSetContainer(rootObjectsToken);
191
192 ············List<IPersistenceCapable> rootObjects = new List<IPersistenceCapable>();
193 ············List<IPersistenceCapable> additionalObjects = new List<IPersistenceCapable>();
194
195 ············foreach (var jObj in rootObjectsToken)
196 ············{
197 ················IPersistenceCapable e = DeserializeObject(jObj);
198 ················if (e != null)
199 ····················rootObjects.Add( e );
200 ············}
201
202 ············foreach (var jObj in additionalObjectsToken)
203 ············{
204 ················IPersistenceCapable e = DeserializeObject(jObj);
205 ················if (e != null)
206 ····················additionalObjects.Add( e );
207 ············}
208
209 ············foreach (var jObj in rootObjectsToken)
210 ············{
211 ················var shortId = (string)jObj["_oid"];
212 ················IPersistenceCapable e = rootObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
213 ················FixRelations( jObj, e, rootObjects, additionalObjects );
214 ············}
215
216 ············foreach (var jObj in additionalObjectsToken)
217 ············{
218 ················var shortId = (string)jObj["_oid"];
219 ················IPersistenceCapable e = additionalObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
220 ················FixRelations( jObj, e, rootObjects, additionalObjects );
221 ············}
222
223 ············return new ArrayList( rootObjects );
224 ········}
225
226 ········internal class Metaclasses
227 ········{
228 ············private static Dictionary<Type, IMetaClass2> theClasses = new Dictionary<Type, IMetaClass2>();
229
230 ············internal static IMetaClass2 GetClass( Type t )
231 ············{
232 ················if (t.IsGenericTypeDefinition)
233 ····················return null;
234
235 ················IMetaClass2 mc;
236
237 ················if (!theClasses.TryGetValue( t, out mc ))
238 ················{
239 ····················lock (theClasses)
240 ····················{
241 ························if (!theClasses.TryGetValue( t, out mc ))··// Threading double check
242 ························{
243 ····························Type mcType = t.GetNestedType( "MetaClass", BindingFlags.NonPublic | BindingFlags.Public );
244 ····························if (null == mcType)
245 ································throw new NDOException( 13, "Missing nested class 'MetaClass' for type '" + t.Name + "'; the type doesn't seem to be enhanced." );
246 ····························Type t2 = mcType;
247 ····························if (t2.IsGenericTypeDefinition)
248 ································t2 = t2.MakeGenericType( t.GetGenericArguments() );
249 ····························var o = Activator.CreateInstance( t2, t );
250 ····························if (o is IMetaClass2 mc2)
251 ································mc = mc2;
252 ····························else
253 ································throw new NDOException( 101010, $"MetaClass for type '{t.FullName}' must implement IMetaClass2, but doesn't. Recompile the assembly with NDO v. >= 4.0.9" );
254 ····························theClasses.Add( t, mc );
255 ························}
256 ····················}
257 ················}
258
259 ················return mc;
260 ············}
261 ········}
262
263
264 ········/// <summary>
265 ········/// Deserializes a Container from a stream.
266 ········/// </summary>
267 ········/// <param name="serializationStream"></param>
268 ········/// <returns></returns>
269 ········public object Deserialize( Stream serializationStream )
270 ········{
271 ············if (this.pm == null)
272 ················throw new NDOException( 20001, "PersistenceManager is not initialized. Provide a PersistenceManager in the formatter constructor." );
273
274 ············JsonSerializer serializer = new JsonSerializer();
275 ············TextReader textReader = new StreamReader( serializationStream );
276 ············var rootObject = (JToken)serializer.Deserialize(textReader, typeof(JToken));
277 ············if (rootObject == null || rootObject.Type == JTokenType.Null)
278 ················return null;
279 ············var result = DeserializeRootArray( rootObject );
280 ············this.pm.UnloadCache();
281 ············return result;
282 ········}
283
284 ········IDictionary<string, object> MakeDict( IPersistenceCapable pc )
285 ········{
 
 
 
286 ············var dict = pc.ToDictionary(pm);
287 var shortId = ( ( IPersistenceCapable) pc) . ShortId( ) ;
288 ············var t = pc.GetType();
289 ············FieldMap fm = new FieldMap(t);
290 var mc = Metaclasses. GetClass( t) ;
 
 
 
 
 
 
 
 
291 ············foreach (var fi in fm.Relations)
292 ············{
293 ················var fiName = fi.Name;
294 if ( ( ( IPersistenceCapable) pc ) . NDOGetLoadState( mc. GetRelationOrdinal( fiName ) ) )
 
 
295 ················{
296 ····················object relationObj = fi.GetValue(pc);
297 ····················if (relationObj is IList list)
298 ····················{
299 ························List<object> dictList = new List<object>();
300 ························foreach (IPersistenceCapable relObject in list)
301 ························{
302 ····························shortId = ( (IPersistenceCapable) relObject ).ShortId();
303 ····························dictList.Add( new { _oid = shortId } );
304 ························}
305 ························dict.Add( fiName, dictList );
306 ····················}
307 ····················else
308 ····················{
309 ························// Hollow object means, that we don't want to transfer the object to the other side.
310 ························if (relationObj == null || ((IPersistenceCapable)relationObj).NDOObjectState == NDOObjectState.Hollow)
311 ························{
312 ····························dict.Add( fiName, null );
313 ························}
314 ························else
315 ························{
316 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
317 ····························shortId = ( (IPersistenceCapable) relIPersistenceCapable ).ShortId();
318 ····························dict.Add( fiName, new { _oid = shortId } );
319 ························}
320 ····················}
321 ················}
322 ············}
323
324 ············return dict;
325 ········}
326
327 ········void InitializePm( object graph )
328 ········{
329 ············IPersistenceCapable pc;
330 ············if (graph is IList list)
331 ············{
332 ················if (list.Count == 0)
333 ····················return;
334 ················pc = (IPersistenceCapable) list[0];
335 ············}
336 ············else
337 ············{
338 ················pc = (IPersistenceCapable) graph;
339 ············}
340
341 ············this.pm = (PersistenceManager) ( pc.NDOStateManager.PersistenceManager );
342 ········}
343
344 ········void RecursivelyAddAdditionalObjects( IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
345 ········{
346 ············var t = e.GetType();
347 ············FieldMap fm = new FieldMap(t);
348 var mc = Metaclasses. GetClass( t) ;
349 ············foreach (var fi in fm.Relations)
350 ············{
351 if ( ( ( IPersistenceCapable) e ) . NDOGetLoadState( mc. GetRelationOrdinal( fi. Name ) ) )
 
 
 
352 ················{
353 ····················object relationObj = fi.GetValue(e);
354 ····················if (relationObj is IList list)
355 ····················{
356 ························List<object> dictList = new List<object>();
357 ························foreach (IPersistenceCapable relIPersistenceCapable in list)
358 ························{
359 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
360 ····························{
361 ································additionalObjects.Add( relIPersistenceCapable );
362 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
363 ····························}
364 ························}
365 ····················}
366 ····················else
367 ····················{
368 ························// Hollow object means, that we don't want to transfer the object to the other side.
369 ························if (relationObj != null && ((IPersistenceCapable)relationObj).NDOObjectState != NDOObjectState.Hollow)
370 ························{
371 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
372 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
373 ····························{
374 ································additionalObjects.Add( relIPersistenceCapable );
375 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
376 ····························}
377 ························}
378 ····················}
379 ················}
380 ············}
381 ········}
382
383 ········object MapRelationChangeRecord(RelationChangeRecord rcr)
384 ········{
385 ············return new
386 ············{
387 ················parent = new { _oid = rcr.Parent.NDOObjectId.ToShortId() },
388 ················child··= new { _oid = rcr.Child.NDOObjectId.ToShortId() },
389 ················isAdded = rcr.IsAdded,
390 ················relationName = rcr.RelationName,
391 ················_oid = "RelationChangeRecord"
392 ············};
393 ········}
394
395 ········void SerializeChangeSet(Stream serializationStream, IList graph)
396 ········{
397 ············// A ChangeSetContainer consists of 4 lists of deleted, added, changed objects, and the relation changes.
398 ············// AddedObjects = new List<IPersistenceCapable>();
399 ············// DeletedObjects = new List<ObjectId>();
400 ············// ChangedObjects = new List<IPersistenceCapable>();
401 ············// RelationChanges = new List<RelationChangeRecord>();
402 ············List<List<object>> resultObjects = new List<List<object>>(graph.Count);
403 ············foreach (IList list in graph)
404 ············{
405 ················List<object> partialResult = new List<object>();
406 ················resultObjects.Add(partialResult);
407 ················foreach (var item in list)
408 ················{
409 ····················if (item is IPersistenceCapable pc)
410 ························partialResult.Add(MakeDict(pc));
411 ····················else if (item is ObjectId oid)
412 ························partialResult.Add(new { _oid = oid.ToShortId() });
413 ····················else if (item is RelationChangeRecord rcr)
414 ························partialResult.Add(MapRelationChangeRecord(rcr));
415 ····················else
416 ························throw new NDOException(20003, $"NDOJsonFormatter: unknown element of type {item.GetType().FullName} in ChangeSetContainer.");
417 ················}
418 ············}
419
420 ············var json = JsonConvert.SerializeObject(new { rootObjects = resultObjects, additionalObjects = new object[] { } });
421 ············var byteArray = Encoding.UTF8.GetBytes(json);
422 ············serializationStream.Write(byteArray, 0, byteArray.Length);
423 ········}
424
425 ········/// <summary>
426 ········/// Serializes an object graph to a stream.
427 ········/// </summary>
428 ········/// <param name="serializationStream"></param>
429 ········/// <param name="graph"></param>
430 ········public void Serialize( Stream serializationStream, object graph )
431 ········{
432 ············string json = null;
433 ············if (this.pm == null)
434 ················InitializePm( graph );
435 ············List<object> rootObjects = new List<object>();
436 ············List<object> additionalObjects = new List<object>();
437 ············List<IPersistenceCapable> rootObjectList = new List<IPersistenceCapable>();
438 ············List<IPersistenceCapable> additionalObjectList = new List<IPersistenceCapable>();
439
440 ············IList list = graph as IList;
441 ············if (list != null)
442 ············{
443 ················if (list.Count > 0 && list[0] is IList)
444 ················{
445 ····················// Change Set
446 ····················SerializeChangeSet(serializationStream, list);
447 ····················return;
448 ················}
449 ················foreach (IPersistenceCapable e in list)
450 ················{
451 ····················rootObjectList.Add( e );
452 ················}
453 ············}
454 ············else if (graph is IPersistenceCapable e)
455 ············{
456 ················rootObjectList.Add( e );
457 ············}
458
459 ············foreach (var e in rootObjectList)
460 ············{
461 ················RecursivelyAddAdditionalObjects( e, rootObjectList, additionalObjectList );
462 ············}
463
464 ············foreach (var e in rootObjectList)
465 ············{
466 ················rootObjects.Add( MakeDict( e ) );
467 ············}
468
469 ············foreach (var e in additionalObjectList)
470 ············{
471 ················additionalObjects.Add( MakeDict( e ) );
472 ············}
473
474 ············json = JsonConvert.SerializeObject( new { rootObjects, additionalObjects } );
475 ············var byteArray = Encoding.UTF8.GetBytes(json);
476 ············serializationStream.Write( byteArray, 0, byteArray.Length );
477 ········}
478 ····}
479 }
480
New Commit (182cfd7)
1 //
2 // Copyright (c) 2002-2024 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.Text;
29 using NDO.ShortId;
30 using NDO.Mapping;
31 using Newtonsoft.Json;
32 using Newtonsoft.Json.Linq;
33 using NDOInterfaces;
34
35 namespace NDO.JsonFormatter
36 {
37 ····/// <summary>
38 ····/// Formatter implementation which serializes NDO ObjectContainers and ChangeSetContainers into Json.
39 ····/// </summary>
40 ····public class NdoJsonFormatter : INdoFormatter
41 ····{
42 ········PersistenceManager pm;
43
44 ········/// <summary>
45 ········/// Constructs an NdoJsonFormatter object. This constructor is sufficient for serialization.
46 ········/// </summary>
47 ········public NdoJsonFormatter()
48 ········{
49 ············this.pm = null;
50 ········}
51
52 ········/// <summary>
53 ········/// Constructs an NdoJsonFormatter object. This constructor should be used for deserialization.
54 ········/// </summary>
55 ········/// <param name="pm"></param>
56 ········public NdoJsonFormatter( PersistenceManager pm )
57 ········{
58 ············this.pm = pm;
59 ········}
60
61 ········IPersistenceCapable DeserializeObject( JToken jobj )
62 ········{
63 ············var shortId = (string)jobj["_oid"];
64
65 ············if (shortId != null)
66 ············{
67 ················var pc = this.pm.FindObject(shortId);
68 ················pc.NDOStateManager = null;··// Detach object
69 ················pc.FromJToken( jobj );
70 ················return pc;
71 ············}
72
73 ············return null;
74 ········}
75
76 ········IPersistenceCapable DeserializeHollowObject(JToken jobj)
77 ········{
78 ············var shortId = (string)jobj["_oid"];
79
80 ············if (shortId != null)
81 ············{
82 ················var pc = this.pm.FindObject(shortId);
83 ················pc.NDOStateManager = null;··// Detach object
84 ················return pc;
85 ············}
86
87 ············return null;
88 ········}
89
90 ········RelationChangeRecord DeserializeRelationChangeRecord(JToken jobj)
91 ········{
92 ············var parent = this.pm.FindObject((string)jobj["parent"]["_oid"]);
93 ············parent.NDOStateManager = null;··// Detach object
94 ············var child = this.pm.FindObject((string)jobj["child"]["_oid"]);
95 ············child.NDOStateManager = null;··// Detach object
96
97 ············var relationName = (string)jobj["relationName"];
98 ············var isAdded = (bool)jobj["isAdded"];
99 ············return new RelationChangeRecord(parent, child, relationName, isAdded);
100 ········}
101
102 ········void FixRelations( JToken jObj, IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
103 ········{
104 ············var t = e.GetType();
105 ············FieldMap fm = new FieldMap(t);
106
107 ············foreach (var fi in fm.Relations)
108 ············{
109 ················var token = jObj[fi.Name];
110 ················if (token == null)
111 ····················continue;
112 ················bool isArray = typeof(IList).IsAssignableFrom( fi.FieldType );
113 ················if (token is JArray jarray)
114 ················{
115 ····················if (isArray)
116 ····················{
117 ························IList container = (IList)fi.GetValue(e);························
118 ························if (container == null)
119 ····························throw new NDOException( 20002, $"Container object of relation {t.Name}.{fi.Name} is not initialized. Please initialize the field in your class constructor." );
120
121 ························container.Clear();
122 ························foreach (var relJObj in jarray)
123 ························{
124 ····························container.Add( DeserializeObject( relJObj ) );
125 ························}
126 ····················}
127 ················}
128 ················else
129 ················{
130 ····················if (!isArray)
131 ····················{
132 ························if (token.Type == JTokenType.Null)
133 ····························fi.SetValue( e, null );
134 ························else
135 ····························fi.SetValue( e, DeserializeObject( token ) );
136 ····················}
137 ················}
138 ············}
139 ········}
140
141 ········IList DeserializeChangeSetContainer(JToken rootArray)
142 ········{
143 ············// 0: AddedObjects = new List<IPersistenceCapable>();
144 ············// 1: DeletedObjects = new List<ObjectId>();
145 ············// 2: ChangedObjects = new List<IPersistenceCapable>();
146 ············// 3: RelationChanges = new List<RelationChangeRecord>();
147
148 ············ArrayList arrayList = new ArrayList(new object[4]);
149
150 ············for (int i = 0; i < 4; i++)
151 ············{
152 ················var partialArray = rootArray[i];
153 ················if (i == 0 || i == 2)
154 ················{
155 ····················var partialList = new List<IPersistenceCapable>();
156 ····················arrayList[i] = partialList;
157 ····················foreach (var item in partialArray)
158 ····················{
159 ························partialList.Add(DeserializeObject(item));
160 ····················}
161 ················}
162 ················if (i == 1)
163 ················{
164 ····················var partialList = new List<IPersistenceCapable>();
165 ····················arrayList[i] = partialList;
166 ····················foreach (var item in partialArray)
167 ····················{
168 ························partialList.Add(DeserializeHollowObject(item));
169 ····················}
170 ················}
171 ················if (i == 3)
172 ················{
173 ····················var partialList = new List<RelationChangeRecord>();
174 ····················arrayList[i] = partialList;
175 ····················foreach (var item in partialArray)
176 ····················{
177 ························partialList.Add(DeserializeRelationChangeRecord(item));
178 ····················}
179 ················}
180 ············}
181
182 ············return arrayList;
183 ········}
184
185 ········object DeserializeRootArray( JToken rootArray )
186 ········{
187 ············var rootObjectsToken = (JArray)rootArray["rootObjects"];
188 ············var additionalObjectsToken = (JArray)rootArray["additionalObjects"];
189 ············if (rootObjectsToken.Count >= 4 && rootObjectsToken[0] is JArray)
190 ················return DeserializeChangeSetContainer(rootObjectsToken);
191
192 ············List<IPersistenceCapable> rootObjects = new List<IPersistenceCapable>();
193 ············List<IPersistenceCapable> additionalObjects = new List<IPersistenceCapable>();
194
195 ············foreach (var jObj in rootObjectsToken)
196 ············{
197 ················IPersistenceCapable e = DeserializeObject(jObj);
198 ················if (e != null)
199 ····················rootObjects.Add( e );
200 ············}
201
202 ············foreach (var jObj in additionalObjectsToken)
203 ············{
204 ················IPersistenceCapable e = DeserializeObject(jObj);
205 ················if (e != null)
206 ····················additionalObjects.Add( e );
207 ············}
208
209 ············foreach (var jObj in rootObjectsToken)
210 ············{
211 ················var shortId = (string)jObj["_oid"];
212 ················IPersistenceCapable e = rootObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
213 ················FixRelations( jObj, e, rootObjects, additionalObjects );
214 ············}
215
216 ············foreach (var jObj in additionalObjectsToken)
217 ············{
218 ················var shortId = (string)jObj["_oid"];
219 ················IPersistenceCapable e = additionalObjects.First(o=>((IPersistenceCapable)o).ShortId() == shortId);
220 ················FixRelations( jObj, e, rootObjects, additionalObjects );
221 ············}
222
223 ············return new ArrayList( rootObjects );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224 ········}
 
 
 
 
 
225
226
227 ········/// <summary>
228 ········/// Deserializes a Container from a stream.
229 ········/// </summary>
230 ········/// <param name="serializationStream"></param>
231 ········/// <returns></returns>
232 ········public object Deserialize( Stream serializationStream )
233 ········{
234 ············if (this.pm == null)
235 ················throw new NDOException( 20001, "PersistenceManager is not initialized. Provide a PersistenceManager in the formatter constructor." );
236
237 ············JsonSerializer serializer = new JsonSerializer();
238 ············TextReader textReader = new StreamReader( serializationStream );
239 ············var rootObject = (JToken)serializer.Deserialize(textReader, typeof(JToken));
240 ············if (rootObject == null || rootObject.Type == JTokenType.Null)
241 ················return null;
242 ············var result = DeserializeRootArray( rootObject );
243 ············this.pm.UnloadCache();
244 ············return result;
245 ········}
246
247 ········IDictionary<string, object> MakeDict( IPersistenceCapable pc )
248 ········{
249 ············var t = pc.GetType();
250 ············if (pc.NDOObjectState == NDOObjectState.Transient)
251 ················throw new Exception( $"Object of type {t.FullName} is transient. It can't be serialized by NDO." );
252 ············var dict = pc.ToDictionary(pm);
253 var shortId = pc. ShortId( ) ;
 
254 ············FieldMap fm = new FieldMap(t);
255
256 ············var mapping = pc.NDOStateManager?.PersistenceManager?.NDOMapping;
257 ············if (mapping == null)
258 ················throw new Exception( $"Can't determine mapping information of class {t.FullName}." );
259
260 ············var cls = mapping.FindClass(t);············
261 ············if (cls == null)
262 ················throw new Exception( $"Can't find class mapping of class {t.FullName}. Check your mapping file." );
263
264 ············foreach (var fi in fm.Relations)
265 ············{
266 ················var fiName = fi.Name;
267 var r = cls. FindRelation( fiName ) ;
268 ················var ordinal = ((ILoadStateSupport) r).Ordinal;
269 ················if (pc.NDOGetLoadState( ordinal ))
270 ················{
271 ····················object relationObj = fi.GetValue(pc);
272 ····················if (relationObj is IList list)
273 ····················{
274 ························List<object> dictList = new List<object>();
275 ························foreach (IPersistenceCapable relObject in list)
276 ························{
277 ····························shortId = ( (IPersistenceCapable) relObject ).ShortId();
278 ····························dictList.Add( new { _oid = shortId } );
279 ························}
280 ························dict.Add( fiName, dictList );
281 ····················}
282 ····················else
283 ····················{
284 ························// Hollow object means, that we don't want to transfer the object to the other side.
285 ························if (relationObj == null || ((IPersistenceCapable)relationObj).NDOObjectState == NDOObjectState.Hollow)
286 ························{
287 ····························dict.Add( fiName, null );
288 ························}
289 ························else
290 ························{
291 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
292 ····························shortId = ( (IPersistenceCapable) relIPersistenceCapable ).ShortId();
293 ····························dict.Add( fiName, new { _oid = shortId } );
294 ························}
295 ····················}
296 ················}
297 ············}
298
299 ············return dict;
300 ········}
301
302 ········void InitializePm( object graph )
303 ········{
304 ············IPersistenceCapable pc;
305 ············if (graph is IList list)
306 ············{
307 ················if (list.Count == 0)
308 ····················return;
309 ················pc = (IPersistenceCapable) list[0];
310 ············}
311 ············else
312 ············{
313 ················pc = (IPersistenceCapable) graph;
314 ············}
315
316 ············this.pm = (PersistenceManager) ( pc.NDOStateManager.PersistenceManager );
317 ········}
318
319 ········void RecursivelyAddAdditionalObjects( IPersistenceCapable e, List<IPersistenceCapable> rootObjects, List<IPersistenceCapable> additionalObjects )
320 ········{
321 ············var t = e.GetType();
322 ············FieldMap fm = new FieldMap(t);
323 var cls = e. NDOStateManager. PersistenceManager. NDOMapping. FindClass( t) ;
324 ············foreach (var fi in fm.Relations)
325 ············{
326 var r = cls. FindRelation( fi. Name) ;
327 ················if (r == null)
328 ····················throw new Exception( $"Can't find relation {fi.Name} in class {t.FullName}" );
329 ················if (e.NDOGetLoadState( ( (ILoadStateSupport) r ).Ordinal ))
330 ················{
331 ····················object relationObj = fi.GetValue(e);
332 ····················if (relationObj is IList list)
333 ····················{
334 ························List<object> dictList = new List<object>();
335 ························foreach (IPersistenceCapable relIPersistenceCapable in list)
336 ························{
337 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
338 ····························{
339 ································additionalObjects.Add( relIPersistenceCapable );
340 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
341 ····························}
342 ························}
343 ····················}
344 ····················else
345 ····················{
346 ························// Hollow object means, that we don't want to transfer the object to the other side.
347 ························if (relationObj != null && ((IPersistenceCapable)relationObj).NDOObjectState != NDOObjectState.Hollow)
348 ························{
349 ····························IPersistenceCapable relIPersistenceCapable = (IPersistenceCapable) relationObj;
350 ····························if (!rootObjects.Contains( relIPersistenceCapable ) && !additionalObjects.Contains( relIPersistenceCapable ))
351 ····························{
352 ································additionalObjects.Add( relIPersistenceCapable );
353 ································RecursivelyAddAdditionalObjects( relIPersistenceCapable, rootObjects, additionalObjects );
354 ····························}
355 ························}
356 ····················}
357 ················}
358 ············}
359 ········}
360
361 ········object MapRelationChangeRecord(RelationChangeRecord rcr)
362 ········{
363 ············return new
364 ············{
365 ················parent = new { _oid = rcr.Parent.NDOObjectId.ToShortId() },
366 ················child··= new { _oid = rcr.Child.NDOObjectId.ToShortId() },
367 ················isAdded = rcr.IsAdded,
368 ················relationName = rcr.RelationName,
369 ················_oid = "RelationChangeRecord"
370 ············};
371 ········}
372
373 ········void SerializeChangeSet(Stream serializationStream, IList graph)
374 ········{
375 ············// A ChangeSetContainer consists of 4 lists of deleted, added, changed objects, and the relation changes.
376 ············// AddedObjects = new List<IPersistenceCapable>();
377 ············// DeletedObjects = new List<ObjectId>();
378 ············// ChangedObjects = new List<IPersistenceCapable>();
379 ············// RelationChanges = new List<RelationChangeRecord>();
380 ············List<List<object>> resultObjects = new List<List<object>>(graph.Count);
381 ············foreach (IList list in graph)
382 ············{
383 ················List<object> partialResult = new List<object>();
384 ················resultObjects.Add(partialResult);
385 ················foreach (var item in list)
386 ················{
387 ····················if (item is IPersistenceCapable pc)
388 ························partialResult.Add(MakeDict(pc));
389 ····················else if (item is ObjectId oid)
390 ························partialResult.Add(new { _oid = oid.ToShortId() });
391 ····················else if (item is RelationChangeRecord rcr)
392 ························partialResult.Add(MapRelationChangeRecord(rcr));
393 ····················else
394 ························throw new NDOException(20003, $"NDOJsonFormatter: unknown element of type {item.GetType().FullName} in ChangeSetContainer.");
395 ················}
396 ············}
397
398 ············var json = JsonConvert.SerializeObject(new { rootObjects = resultObjects, additionalObjects = new object[] { } });
399 ············var byteArray = Encoding.UTF8.GetBytes(json);
400 ············serializationStream.Write(byteArray, 0, byteArray.Length);
401 ········}
402
403 ········/// <summary>
404 ········/// Serializes an object graph to a stream.
405 ········/// </summary>
406 ········/// <param name="serializationStream"></param>
407 ········/// <param name="graph"></param>
408 ········public void Serialize( Stream serializationStream, object graph )
409 ········{
410 ············string json = null;
411 ············if (this.pm == null)
412 ················InitializePm( graph );
413 ············List<object> rootObjects = new List<object>();
414 ············List<object> additionalObjects = new List<object>();
415 ············List<IPersistenceCapable> rootObjectList = new List<IPersistenceCapable>();
416 ············List<IPersistenceCapable> additionalObjectList = new List<IPersistenceCapable>();
417
418 ············IList list = graph as IList;
419 ············if (list != null)
420 ············{
421 ················if (list.Count > 0 && list[0] is IList)
422 ················{
423 ····················// Change Set
424 ····················SerializeChangeSet(serializationStream, list);
425 ····················return;
426 ················}
427 ················foreach (IPersistenceCapable e in list)
428 ················{
429 ····················rootObjectList.Add( e );
430 ················}
431 ············}
432 ············else if (graph is IPersistenceCapable e)
433 ············{
434 ················rootObjectList.Add( e );
435 ············}
436
437 ············foreach (var e in rootObjectList)
438 ············{
439 ················RecursivelyAddAdditionalObjects( e, rootObjectList, additionalObjectList );
440 ············}
441
442 ············foreach (var e in rootObjectList)
443 ············{
444 ················rootObjects.Add( MakeDict( e ) );
445 ············}
446
447 ············foreach (var e in additionalObjectList)
448 ············{
449 ················additionalObjects.Add( MakeDict( e ) );
450 ············}
451
452 ············json = JsonConvert.SerializeObject( new { rootObjects, additionalObjects } );
453 ············var byteArray = Encoding.UTF8.GetBytes(json);
454 ············serializationStream.Write( byteArray, 0, byteArray.Length );
455 ········}
456 ····}
457 }
458