/// <summary> /// Parse an <see cref="IStorable"/> listing all its children /// and the ones that changed. /// </summary> /// <param name="children">List of children.</param> /// <param name="changedStorables">List of storables with changes.</param> /// <param name="storable">The storable object to parse.</param> /// <param name="settings">The serialization settings.</param> /// <param name="reset">If set to <c>true</c> reset the IsChanged flag.</param> public bool Parse(out StorableNode parentNode, IStorable storable, JsonSerializerSettings settings, bool reset = true) { bool ret = ParseInternal(out parentNode, storable, settings, reset); if (ret && current != null) { Log.Error("Stack should be empty"); return(false); } parentNode.OrphanChildren = deletedStorables.Except(aliveStorables).ToList(); deletedStorables.Clear(); aliveStorables.Clear(); return(ret); }
internal bool ParseInternal(out StorableNode parentNode, IStorable value, JsonSerializerSettings settings, bool reset = true) { stack = new Stack <object> (); parentNode = new StorableNode(value); deletedStorables = new HashSet <IStorable> (); parsedCount = 0; aliveStorables = new HashSet <IStorable> (); resolver = settings.ContractResolver ?? new DefaultContractResolver(); this.reset = reset; try { CheckValue(value, parentNode); return(true); } catch (Exception ex) { Log.Exception(ex); return(false); } }
void CheckValue(object value, StorableNode node = null) { IStorable storable; if (value == null) { return; } if (stack.Any(o => Object.ReferenceEquals(o, value))) { // Value in stack, return to avoid dependency cycles. return; } stack.Push(value); storable = value as IStorable; if (storable != null) { if (node == null) { node = new StorableNode(storable); } // Update parent and children relations if (current != null) { if (current.Deleted) { node.Deleted = true; } else { current.Children.Add(node); } } if (!node.Deleted) { aliveStorables.Add(storable); } node.Parent = current; current = node; } // Figure out the type of object we are dealing with and parse it accordingly. // Primitives are ignored (not being objects containers) and lists and dictionaries // are traversed through all their children. JsonContract valueContract = resolver.ResolveContract(value.GetType()); if (valueContract is JsonObjectContract) { CheckObject(value, valueContract as JsonObjectContract); } else if (valueContract is JsonArrayContract) { CheckEnumerable(value as IEnumerable, valueContract as JsonArrayContract); } else if (valueContract is JsonDictionaryContract) { CheckEnumerable((value as IDictionary).Values, valueContract as JsonArrayContract); } else { // Skip primitive value } // Now try to find orphaned objects and create nodes with the Deleted flags. // These objects are currently saved in the database and cached in IStorable.SavedChildren but // the current IStorable does not reference them anymore as real children. // We used this cache to find the orphaned children and mark them with the Deleted flag. if (storable != null) { if (storable.DeleteChildren && storable.SavedChildren != null) { var orphaned = storable.SavedChildren.Except(node.Children.Select(n => n.Storable)); foreach (IStorable st in orphaned) { deletedStorables.Add(st); StorableNode onode = new StorableNode(st); onode.Deleted = true; CheckValue(st, onode); } } current = current.Parent; } stack.Pop(); }