private object ReadJsonAsResourceObject(ForkableJsonReader reader, Type objectType, JsonSerializer serializer) { // if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } var serializationData = SerializationData.GetSerializationData(reader); var jsonApiContractResolver = (JsonApiContractResolver)serializer.ContractResolver; var reference = ReaderUtil.ReadAheadToIdentifyObject(reader); if (serializationData.Included.TryGetValue(reference, out object resourceObject)) { if (resourceObject is JObject resoruceObjectJObject) { // sometimes the value in the reference resolver is a JObject. This occurs when we // did not know what type it should be when we first read it (i.e. included was processed // before the item). In these cases we now know what type it should be so will read it // as such var resourceObjectReader = new ForkableJsonReader(resoruceObjectJObject.CreateReader(), reader.SerializationDataToken); resourceObjectReader.Read(); //JObject readers begin at Not Started resourceObject = jsonApiContractResolver.ResourceObjectConverter.ReadJson( resourceObjectReader, objectType, null, serializer); } //push the reader to the end, we dont need anything else out of the reference ReaderUtil.ReadUntilEnd(reader, reader.Path); } else { var contract = (JsonObjectContract)jsonApiContractResolver.ResolveContract(objectType); resourceObject = ReaderUtil.CreateObject(serializationData, objectType, reference.Type, serializer); // for placeholders we will just read the top level properties // it is unlikely to have attributes/relationships present foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, resourceObject, contract.Properties.GetClosestMatchProperty(propName), reader); } serializationData.Included[reference] = resourceObject; } if (!TypeInfoShim.IsInstanceOf(objectType.GetTypeInfo(), resourceObject)) { throw new JsonSerializationException($"Unable to assign object '{resourceObject}' to type '{objectType}' at path {reader.FullPath}"); } return(resourceObject); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var serializationData = SerializationData.GetSerializationData(reader); reader = new ForkableJsonReader(reader); var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var rootObject = contract.DefaultCreator(); serializationData.HasProcessedDocumentRoot = true; var includedConverter = new IncludedConverter(); foreach (var propName in ReaderUtil.IterateProperties(reader)) { switch (propName) { case PropertyNames.Data: var documentRootInterfaceType = TypeInfoShim.GetInterfaces(objectType.GetTypeInfo()) .Select(x => x.GetTypeInfo()) .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDocumentRoot <>)); var dataType = documentRootInterfaceType.GenericTypeArguments[0]; var dataObj = serializer.Deserialize(reader, dataType); contract.Properties.GetClosestMatchProperty(PropertyNames.Data).ValueProvider.SetValue(rootObject, dataObj); break; case PropertyNames.Included: //if our object has an included property we will do our best to populate it var property = contract.Properties.GetClosestMatchProperty(propName); if (ReaderUtil.CanPopulateProperty(property)) { ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), ((ForkableJsonReader)reader).Fork()); } //still need to read our values so they are updated foreach (var obj in ReaderUtil.IterateList(reader)) { var includedObject = includedConverter.ReadJson(reader, typeof(object), null, serializer); } break; default: ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), reader); break; } } return(rootObject); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jsonApiContractResolver = (JsonApiContractResolver)serializer.ContractResolver; var contract = jsonApiContractResolver.ResolveContract(objectType); // we be a ResourceObject rather than a RelationshpObject // if so we will just read the data property of the resource object if (!(contract is ResourceRelationshipContract rrc)) { return(ReadJsonDataPropertyAsResourceObject(reader, objectType, existingValue, serializer, jsonApiContractResolver)); } if (ReaderUtil.TryUseCustomConvertor(reader, objectType, existingValue, serializer, this, out object customConvertedValue)) { return(customConvertedValue); } // if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } //create a new relationship object and start populating the properties var obj = rrc.DefaultCreator(); foreach (var propName in ReaderUtil.IterateProperties(reader)) { switch (propName) { case PropertyNames.Data: ReaderUtil.TryPopulateProperty( serializer, obj, rrc.Properties.GetClosestMatchProperty(propName), reader, overrideConverter: jsonApiContractResolver.ResourceIdentifierConverter); break; default: ReaderUtil.TryPopulateProperty( serializer, obj, rrc.Properties.GetClosestMatchProperty(propName), reader); break; } } return(obj); }
private object ReadJsonAsExplicitResourceIdentifier(ForkableJsonReader reader, Type objectType, JsonSerializer serializer) { if (ReaderUtil.TryUseCustomConvertor(reader, objectType, null, serializer, this, out object customConvertedValue)) { return(customConvertedValue); } // if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } var serializationData = SerializationData.GetSerializationData(reader); var jsonApiContractResolver = (JsonApiContractResolver)serializer.ContractResolver; var resourceIdentifierContract = (ResourceIdentifierContract)jsonApiContractResolver.ResolveContract(objectType); var resourceIdentifier = resourceIdentifierContract.DefaultCreator(); var reference = ReaderUtil.ReadAheadToIdentifyObject(reader); var explicitResourceIdentifierReader = reader.Fork(); foreach (var innerPropName in ReaderUtil.IterateProperties(explicitResourceIdentifierReader)) { ReaderUtil.TryPopulateProperty( serializer, resourceIdentifier, resourceIdentifierContract.Properties.GetClosestMatchProperty(innerPropName), explicitResourceIdentifierReader); } var resourceObject = ReadJsonAsResourceObject( reader, resourceIdentifierContract.ResourceObjectProperty.PropertyType, serializer); //we will only set the resource object if we have rendered the included //value somehwere, if we have not it means the value was actaully provided var valueProvider = resourceIdentifierContract.ResourceObjectProperty.ValueProvider; serializationData.PostProcessingActions.Add(() => { if (serializationData.RenderedIncluded.Contains(reference)) { valueProvider.SetValue(resourceIdentifier, resourceObject); } }); return(resourceIdentifier); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { reader = new ForkableJsonReader(reader); var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var rootObject = contract.DefaultCreator(); serializer.ReferenceResolver.AddReference(null, IncludedReferenceResolver.RootReference, rootObject); var includedConverter = new IncludedConverter(); foreach (var propName in ReaderUtil.IterateProperties(reader)) { switch (propName) { case PropertyNames.Data: var documentRootInterfaceType = TypeInfoShim.GetInterfaces(objectType.GetTypeInfo()) .Select(x => x.GetTypeInfo()) .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDocumentRoot <>)); var dataType = documentRootInterfaceType.GenericTypeArguments[0]; var dataObj = serializer.Deserialize(reader, dataType); contract.Properties.GetClosestMatchProperty(PropertyNames.Data).ValueProvider.SetValue(rootObject, dataObj); break; case PropertyNames.Included: foreach (var obj in ReaderUtil.IterateList(reader)) { var includedObject = includedConverter.ReadJson(reader, typeof(object), null, serializer); } break; default: ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), reader); break; } } return(rootObject); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root IEnumerable <IError> errors; if (DocumentRootConverter.TryResolveAsRootError(reader, objectType, serializer, out errors)) { //not sure if this is the correct thing to do. We are deserializing a single //error but json:api always gives us a list of errors. We just return the first //error return(errors.First()); } var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var error = (IError)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); foreach (var propName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty(serializer, error, contract.Properties.GetClosestMatchProperty(propName), reader); } return(error); }
private static object ReadJsonDataPropertyAsResourceObject(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer, JsonApiContractResolver jsonApiContractResolver) { object resourceObject = null; var isValid = false; foreach (var propName in ReaderUtil.IterateProperties(reader)) { switch (propName) { case PropertyNames.Data: isValid = true; // let the resource identifier deal with the rest resourceObject = jsonApiContractResolver.ResourceIdentifierConverter.ReadJson( reader, objectType, existingValue, serializer); break; case PropertyNames.Links: case PropertyNames.Meta: isValid = true; break; default: break; } } if (!isValid) { var path = (reader as ForkableJsonReader)?.FullPath ?? reader.Path; throw new JsonApiFormatException(path, $"Expected to find one of links, data or meta on relationship object", "A relationship object MUST contain at least one of: links, data or meta"); } return(resourceObject); }
protected void PopulateProperties(JsonSerializer serializer, object obj, JsonReader reader) { JsonObjectContract contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(obj.GetType()); foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(propName), reader); //flatten out attributes onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Attributes) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } //flatten out relationships onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Relationships) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } } }
protected void PopulateProperties(JsonSerializer serializer, object obj, JsonReader reader, JsonObjectContract contract) { foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(propName), reader); //flatten out attributes onto the object if (!successfullyPopulateProperty && propName == "attributes") { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } //flatten out relationships onto the object if (!successfullyPopulateProperty && propName == "relationships") { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root var serializationData = SerializationData.GetSerializationData(reader); if (!serializationData.HasProcessedDocumentRoot) { return(DocumentRootConverter.ResolveAsRootData(reader, objectType, serializer)); } if (ReaderUtil.TryUseCustomConvertor(reader, objectType, existingValue, serializer, this, out object customConvertedValue)) { return(customConvertedValue); } //if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } serializationData.ConverterStack.Push(this); var forkableReader = reader as ForkableJsonReader ?? new ForkableJsonReader(reader); reader = forkableReader; var contractResolver = (JsonApiContractResolver)serializer.ContractResolver; var reference = ReaderUtil.ReadAheadToIdentifyObject(forkableReader); //if we dont have this object already we will create it existingValue = existingValue ?? CreateObject(objectType, reference.Type, serializer); JsonContract rawContract = contractResolver.ResolveContract(existingValue.GetType()); if (!(rawContract is JsonObjectContract contract)) { throw new JsonApiFormatException( forkableReader.FullPath, $"Expected created object to be a resource object, but found '{existingValue}'", "Resource indentifier objects MUST contain 'id' members"); } foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(propName), reader); //flatten out attributes onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Attributes) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } //flatten out relationships onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Relationships) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { var prop = contract.Properties.GetClosestMatchProperty(innerPropName); if (prop == null) { continue; } // This is a massive hack. MemberConverters used to work and allowed // modifying a members create object. Unfortunatly Resource Identifiers // are no longer created by this object converter. We are passing the // convertor via the serialization data so ResourceIdentifierConverter // can access it down the line. // next breaking change remove support for ResourceObjectConverter // member converters if (prop.MemberConverter != null) { serializationData.ConverterStack.Push(prop.MemberConverter); } ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(innerPropName), reader, overrideConverter: contractResolver.ResourceRelationshipConverter); if (prop.MemberConverter != null) { serializationData.ConverterStack.Pop(); } } } } //we have rendered this so we will tag it as included serializationData.RenderedIncluded.Add(reference); serializationData.Included[reference] = existingValue; serializationData.ConverterStack.Pop(); return(existingValue); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var rootObject = contract.DefaultCreator(); serializer.ReferenceResolver.AddReference(null, IncludedReferenceResolver.RootReference, rootObject); //var includedConverter = new IncludedConverter(); foreach (var propName in ReaderUtil.IterateProperties(reader)) { switch (propName) { case PropertyNames.Data: var documentRootInterfaceType = TypeInfoShim.GetInterfaces(objectType.GetTypeInfo()) .Select(x => x.GetTypeInfo()) .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDocumentRoot <>)); var dataType = documentRootInterfaceType.GenericTypeArguments[0]; var dataObj = serializer.Deserialize(reader, dataType); contract.Properties.GetClosestMatchProperty(PropertyNames.Data).ValueProvider.SetValue(rootObject, dataObj); break; case PropertyNames.Included: // //if our object has an included property we will do our best to populate it // var property = contract.Properties.GetClosestMatchProperty(propName); // //if (ReaderUtil.CanPopulateProperty(property)) // //{ // // ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), ((ForkableJsonReader)reader).Fork()); // //} //still need to read our values so they are updated foreach (var obj in ReaderUtil.IterateList(reader)) { var type = ""; var id = ""; //read untill id and type foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { switch (innerPropName) { case PropertyNames.Type: type = reader.Value.ToString(); break; case PropertyNames.Id: id = reader.Value.ToString(); break; default: break; } if (!string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(id)) { break; } } var existingObject = serializer.ReferenceResolver.ResolveReference(null, type + ":" + id); if (existingObject != null) { //We have an existing object, its likely our included data has more detail than what //is currently on the object so we will pass the reader so it can be deserialized again var inc_type = existingObject.GetType(); var existingObjectContract = serializer.ContractResolver.ResolveContract(inc_type); existingObjectContract.Converter.ReadJson(reader, inc_type, existingObject, serializer); } //contract.Converter.ReadJson(reader, typeof(object), rootObject, serializer); //var includedObject = includedConverter.ReadJson(reader, typeof(object), null, serializer); } break; default: ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), reader); break; } } return(rootObject); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root var serializationData = SerializationData.GetSerializationData(reader); if (!serializationData.HasProcessedDocumentRoot) { return(DocumentRootConverter.ResolveAsRootData(reader, objectType, serializer)); } if (ReaderUtil.TryUseCustomConvertor(reader, objectType, existingValue, serializer, this, out object customConvertedValue)) { return(customConvertedValue); } //if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } serializationData.ConverterStack.Push(this); var forkableReader = reader as ForkableJsonReader ?? new ForkableJsonReader(reader); reader = forkableReader; var contractResolver = (JsonApiContractResolver)serializer.ContractResolver; var reference = ReaderUtil.ReadAheadToIdentifyObject(forkableReader); //if we dont have this object already we will create it existingValue = existingValue ?? CreateObject(objectType, reference.Type, serializer); //mark this object as a possible include. We need to do this before deserialiazing //the relationship; It could have relationships that reference back to this object serializationData.Included[reference] = existingValue; JsonContract rawContract = contractResolver.ResolveContract(existingValue.GetType()); if (!(rawContract is JsonObjectContract contract)) { throw new JsonApiFormatException( forkableReader.FullPath, $"Expected created object to be a resource object, but found '{existingValue}'", "Resource indentifier objects MUST contain 'id' members"); } foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(propName), reader); //flatten out attributes onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Attributes) { // AH 2020-08-27 - store unknown Attributes in this dictionary. Dictionary <string, object> extraAttributes = new Dictionary <string, object>(); JsonProperty extraAttributesProperty = contract.Properties.GetClosestMatchProperty("extraAttributes"); foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { JsonProperty matchedProperty = contract.Properties.GetClosestMatchProperty(innerPropName); // regular behavior if (matchedProperty != null) { ReaderUtil.TryPopulateProperty( serializer, existingValue, matchedProperty, //contract.Properties.GetClosestMatchProperty(innerPropName), reader); } // modified behavior - store any unknown custom attributes into an extraAttributes collection else if (extraAttributesProperty != null) { object extraAttributeValue = null; // this is a Custom Attribute or an Attribute that is a list if (reader.TokenType == JsonToken.StartArray) { JArray jarr = JArray.Load(reader); if (jarr.Count != 0) { // if we have an object it's a custom attribute. if (jarr[0].Type == JTokenType.Object) { JObject jobj = jarr[0].ToObject <JObject>(); extraAttributeValue = jobj; } //otherwise the array itself is the value for the attribute (like entity_types.entity_attributes) else { extraAttributeValue = jarr; } } // don't read here. Next iteration expects us to be at the end of array/object //reader.Read(); } // It's a regular Attribute that we don't expect. Just store it's value. else { extraAttributeValue = reader.Value; } extraAttributes[innerPropName] = extraAttributeValue; } } if (extraAttributesProperty != null) { extraAttributesProperty.ValueProvider.SetValue(existingValue, extraAttributes); } } //flatten out relationships onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Relationships) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { var prop = contract.Properties.GetClosestMatchProperty(innerPropName); if (prop == null) { continue; } // This is a massive hack. MemberConverters used to work and allowed // modifying a members create object. Unfortunatly Resource Identifiers // are no longer created by this object converter. We are passing the // convertor via the serialization data so ResourceIdentifierConverter // can access it down the line. // next breaking change remove support for ResourceObjectConverter // member converters if (prop.MemberConverter != null) { serializationData.ConverterStack.Push(prop.MemberConverter); } ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(innerPropName), reader, overrideConverter: contractResolver.ResourceRelationshipConverter); if (prop.MemberConverter != null) { serializationData.ConverterStack.Pop(); } } } } //we have read the object so we will tag it as 'rendered' serializationData.RenderedIncluded.Add(reference); serializationData.ConverterStack.Pop(); return(existingValue); }
protected object PopulateProperties(JsonSerializer serializer, object obj, JsonReader reader, JsonObjectContract contract) { foreach (var propName in ReaderUtil.IterateProperties(reader)) { if (propName == PropertyNames.Type) { var type = reader.Value; if (obj.GetType().Name.ToLower() != type.ToString().ToLower()) { Assembly[] assembly = Utility.GetAssemblies(); var tp = assembly.SelectMany(s => s.GetTypes()).ToList().Where(t => t.Name.ToLower() == type.ToString().ToLower()).SingleOrDefault(); if (tp != null) { obj = Activator.CreateInstance(tp); contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(obj.GetType()); } } //retObj.id = obj; } var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(propName), reader); if (!successfullyPopulateProperty) { //flatten out attributes onto the object if (propName == "attributes") { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } //flatten out relationships onto the object if (propName == "relationships") { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { //read into the 'Data' path var preDataPath = ReaderUtil.ReadUntilStart(reader, DataPathRegex); ReaderUtil.TryPopulateProperty( serializer, obj, contract.Properties.GetClosestMatchProperty(innerPropName), reader); //read out of the 'Data' path ReaderUtil.ReadUntilEnd(reader, preDataPath); } } } } return(obj); }