private void DeserializeArrayNodeGeneric <TElement>(IDeserializerNode node, Reader reader) { if (TryDeserializeAsReference(node, reader)) { return; } var jarr = reader.Token as JArray; if (jarr == null) { throw new PomonaSerializationException("Expected JSON token of type array, but was " + reader.Token.Type); } var expectedBaseType = node.ExpectedBaseType; // Deserialize as object array by default bool asArray; TypeSpec elementType; if (expectedBaseType != null && expectedBaseType.IsCollection) { elementType = expectedBaseType.ElementType; asArray = expectedBaseType.IsArray; } else { elementType = node.Context.GetClassMapping(typeof(object)); asArray = true; } bool isPatching; ICollection <TElement> collection; if (node.Value == null) { if (expectedBaseType != null && expectedBaseType == typeof(ISet <TElement>)) { collection = new HashSet <TElement>(); } else { collection = new List <TElement>(); } isPatching = false; } else { collection = (ICollection <TElement>)node.Value; isPatching = true; } if (isPatching && node.Operation == DeserializerNodeOperation.Post) { // Clear list and add new items node.CheckItemAccessRights(HttpMethod.Delete); collection.Clear(); } foreach (var jitem in jarr) { var jobj = jitem as JObject; var itemNode = new ItemValueDeserializerNode(elementType, node.Context, node.ExpandPath, node); if (jobj != null) { foreach (var jprop in jobj.Properties().Where(IsIdentifierProperty)) { // Starts with "-@" or "*@" var identifyPropName = jprop.Name.Substring(2); var identifyProp = itemNode.ValueType.Properties.FirstOrDefault(x => x.JsonName == identifyPropName); if (identifyProp == null) { throw new PomonaSerializationException("Unable to find predicate property " + jprop.Name + " in object"); } var identifierNode = new ItemValueDeserializerNode(identifyProp.PropertyType, itemNode.Context, parent: itemNode); DeserializeThroughContext(identifierNode, new Reader(jprop.Value)); var identifierValue = identifierNode.Value; if (jprop.Name[0] == '-') { itemNode.Operation = DeserializerNodeOperation.Delete; } else if (jprop.Name[0] == '*') { itemNode.Operation = DeserializerNodeOperation.Patch; } else { throw new PomonaSerializationException("Unexpected json patch identifier property."); } itemNode.Value = collection.Cast <object>().First( x => identifierValue.Equals(identifyProp.GetValue(x, itemNode.Context))); } } if (itemNode.Operation == DeserializerNodeOperation.Delete) { node.CheckItemAccessRights(HttpMethod.Delete); collection.Remove((TElement)itemNode.Value); } else { if (itemNode.Operation == DeserializerNodeOperation.Patch) { node.CheckItemAccessRights(HttpMethod.Patch); } else if (isPatching) { node.CheckAccessRights(HttpMethod.Post); } DeserializeThroughContext(itemNode, new Reader(jitem)); if (itemNode.Operation != DeserializerNodeOperation.Patch) { if (!(itemNode.ExpectedBaseType is StructuredType) || itemNode.ExpectedBaseType.IsAnonymous() || !collection.Contains((TElement)itemNode.Value)) { collection.Add((TElement)itemNode.Value); } } } } if (node.Value == null) { node.Value = asArray ? collection.ToArray() : collection; } }