internal void Serialize(PropertyMapping prop, object instance, bool summary, ComplexTypeWriter.SerializationMode mode) { if (prop == null) throw Error.ArgumentNull("prop"); // ArrayMode avoid the dispatcher making nested calls into the RepeatingElementWriter again // when writing array elements. FHIR does not support nested arrays, and this avoids an endlessly // nesting series of dispatcher calls if (prop.IsCollection) { var elements = instance as IList; if (elements == null) throw Error.Argument("existing", "Can only write repeating elements from a type implementing IList"); _current.WriteStartArray(); foreach (var element in elements) { if (element == null) throw Error.Format("The FHIR serialization does not support arrays with empty (null) elements", null); write(prop, element, summary, mode); } _current.WriteEndArray(); } else write(prop, instance, summary, mode); }
private void write(PropertyMapping prop, object instance, bool summary, ComplexTypeWriter.SerializationMode mode) { // If this is a primitive type, no classmappings and reflection is involved, // just serialize the primitive to the writer if (prop.IsPrimitive) { var writer = new PrimitiveValueWriter(_current); writer.Serialize(instance, prop.SerializationHint); return; } // A Choice property that contains a choice of any resource // (as used in Resource.contained) if (prop.Choice == ChoiceType.ResourceChoice) { var writer = new ResourceWriter(_current); writer.Serialize(instance, summary, contained: true); return; } ClassMapping mapping = _inspector.ImportType(instance.GetType()); if (mode == ComplexTypeWriter.SerializationMode.AllMembers || mode == ComplexTypeWriter.SerializationMode.NonValueElements) { var cplxWriter = new ComplexTypeWriter(_current); cplxWriter.Serialize(mapping, instance, summary, mode); } else { object value = mapping.PrimitiveValueProperty.GetValue(instance); write(mapping.PrimitiveValueProperty, value, summary, ComplexTypeWriter.SerializationMode.AllMembers); } }
public object Deserialize(PropertyMapping prop, string memberName, object existing=null) { if (prop == null) throw Error.ArgumentNull("prop"); if (existing != null && !(existing is IList) ) throw Error.Argument("existing", "Can only read repeating elements into a type implementing IList"); IList result = existing as IList; bool overwriteMode; IEnumerable<IFhirReader> elements; if(_current.CurrentToken == TokenType.Array) // Json has members that are arrays, if we encounter multiple, update the old values of the array { overwriteMode = result != null && result.Count > 0; elements = _current.GetArrayElements(); } else if(_current.CurrentToken == TokenType.Object) // Xml has repeating members, so this results in an "array" of just 1 member { //TODO: This makes member : {x} in Json valid too, //even if json should have member : [{x}] overwriteMode = false; elements = new List<IFhirReader>() { _current }; } else throw Error.Format("Expecting to be either at a repeating complex element or an array when parsing a repeating member.", _current); if (result == null) result = ReflectionHelper.CreateGenericList(prop.ElementType); var position = 0; foreach(var element in elements) { var reader = new DispatchingReader(element, arrayMode: true); if(overwriteMode) { if (position >= result.Count) throw Error.Format("The value and extension array are not well-aligned", _current); // Arrays may contain null values as placeholders if(element.CurrentToken != TokenType.Null) result[position] = reader.Deserialize(prop, memberName, existing: result[position]); } else { object item = null; if (element.CurrentToken != TokenType.Null) item = reader.Deserialize(prop, memberName); else item = null; // Arrays may contain null values as placeholders result.Add(item); } position++; } return result; }
public IList Deserialize(PropertyMapping prop, string memberName, IList existing=null) { if (prop == null) throw Error.ArgumentNull("prop"); IList result = existing; if (result == null) result = ReflectionHelper.CreateGenericList(prop.ElementType); var reader = new DispatchingReader(_current, arrayMode: true); result.Add(reader.Deserialize(prop, memberName)); return result; }
public object Deserialize(PropertyMapping prop, string memberName, object existing=null) { if (prop == null) throw Error.ArgumentNull("prop"); // ArrayMode avoid the dispatcher making nested calls into the RepeatingElementReader again // when reading array elements. FHIR does not support nested arrays, and this avoids an endlessly // nesting series of dispatcher calls if (!_arrayMode && prop.IsCollection) { if (existing != null && !(existing is IList) ) throw Error.Argument("existing", "Can only read repeating elements into a type implementing IList"); var reader = new RepeatingElementReader(_current); return reader.Deserialize(prop, memberName, (IList)existing); } // If this is a primitive type, no classmappings and reflection is involved, // just parse the primitive from the input // NB: no choices for primitives! if(prop.IsPrimitive) { var reader = new PrimitiveValueReader(_current); return reader.Deserialize(prop.ElementType); } // A Choice property that contains a choice of any resource // (as used in Resource.contained) if(prop.Choice == ChoiceType.ResourceChoice) { var reader = new ResourceReader(_current); return reader.Deserialize(null); } ClassMapping mapping; // Handle other Choices having any datatype or a list of datatypes if(prop.Choice == ChoiceType.DatatypeChoice) { // For Choice properties, determine the actual type of the element using // the suffix of the membername (i.e. deceasedBoolean, deceasedDate) // This function implements type substitution. mapping = determineElementPropertyType(prop, memberName); } // Else use the actual return type of the property else { mapping = _inspector.ImportType(prop.ElementType); } if (existing != null && !(existing is Resource) && !(existing is Element) ) throw Error.Argument("existing", "Can only read complex elements into types that are Element or Resource"); var cplxReader = new ComplexTypeReader(_current); return cplxReader.Deserialize(mapping, (Base)existing); }
internal static PropertyMapping Create(PropertyInfo prop, out IEnumerable<Type> referredTypes) { if (prop == null) throw Error.ArgumentNull("prop"); var foundTypes = new List<Type>(); PropertyMapping result = new PropertyMapping(); #if PORTABLE45 var elementAttr = prop.GetCustomAttribute<FhirElementAttribute>(); #else var elementAttr = (FhirElementAttribute)Attribute.GetCustomAttribute(prop, typeof(FhirElementAttribute)); #endif result.Name = determinePropertyName(prop); result.ReturnType = prop.PropertyType; result.ElementType = result.ReturnType; result.InSummary = elementAttr != null ? elementAttr.InSummary : false; result.Choice = elementAttr != null ? elementAttr.Choice : ChoiceType.None; if (elementAttr != null) { result.SerializationHint = elementAttr.XmlSerialization; result.Order = elementAttr.Order; } foundTypes.Add(result.ElementType); result.IsCollection = ReflectionHelper.IsTypedCollection(prop.PropertyType) && !prop.PropertyType.IsArray; // Get to the actual (native) type representing this element if (result.IsCollection) result.ElementType = ReflectionHelper.GetCollectionItemType(prop.PropertyType); if (ReflectionHelper.IsNullableType(result.ElementType)) result.ElementType = ReflectionHelper.GetNullableArgument(result.ElementType); result.IsPrimitive = isAllowedNativeTypeForDataTypeValue(result.ElementType); // Check wether this property represents a native .NET type // marked to receive the class' primitive value in the fhir serialization // (e.g. the value from the Xml 'value' attribute or the Json primitive member value) if(result.IsPrimitive) result.RepresentsValueElement = isPrimitiveValueElement(prop); referredTypes = foundTypes; // May need to generate getters/setters using pre-compiled expression trees for performance. // See http://weblogs.asp.net/marianor/archive/2009/04/10/using-expression-trees-to-get-property-getter-and-setters.aspx result._getter = instance => prop.GetValue(instance, null); result._setter = (instance,value) => prop.SetValue(instance, value, null); return result; }
internal static PropertyMapping Create(PropertyInfo prop, out IEnumerable<Type> referredTypes) { if (prop == null) throw Error.ArgumentNull("prop"); var foundTypes = new List<Type>(); PropertyMapping result = new PropertyMapping(); #if (PORTABLE45 || NETCOREAPP1_1) var elementAttr = prop.GetCustomAttribute<FhirElementAttribute>(); #else var elementAttr = (FhirElementAttribute)Attribute.GetCustomAttribute(prop, typeof(FhirElementAttribute)); #endif result.Name = determinePropertyName(prop); result.ReturnType = prop.PropertyType; result.ElementType = result.ReturnType; result.InSummary = elementAttr != null ? elementAttr.InSummary : false; result.Choice = elementAttr != null ? elementAttr.Choice : ChoiceType.None; if (elementAttr != null) { result.SerializationHint = elementAttr.XmlSerialization; result.Order = elementAttr.Order; } foundTypes.Add(result.ElementType); result.IsCollection = ReflectionHelper.IsTypedCollection(prop.PropertyType) && !prop.PropertyType.IsArray; // Get to the actual (native) type representing this element if (result.IsCollection) result.ElementType = ReflectionHelper.GetCollectionItemType(prop.PropertyType); if (ReflectionHelper.IsNullableType(result.ElementType)) result.ElementType = ReflectionHelper.GetNullableArgument(result.ElementType); result.IsPrimitive = isAllowedNativeTypeForDataTypeValue(result.ElementType); // Check wether this property represents a native .NET type // marked to receive the class' primitive value in the fhir serialization // (e.g. the value from the Xml 'value' attribute or the Json primitive member value) if(result.IsPrimitive) result.RepresentsValueElement = isPrimitiveValueElement(prop); referredTypes = foundTypes; // May need to generate getters/setters using pre-compiled expression trees for performance. // See http://weblogs.asp.net/marianor/archive/2009/04/10/using-expression-trees-to-get-property-getter-and-setters.aspx result._getter = instance => prop.GetValue(instance, null); result._setter = (instance,value) => prop.SetValue(instance, value, null); return result; }
public IList Deserialize(PropertyMapping prop, string memberName, IList existing=null) { if (prop == null) throw Error.ArgumentNull("prop"); IList result = existing; if(_current.CurrentToken != TokenType.Object) // Xml has repeating members, so this results in an "array" of just 1 member throw Error.Format("Expecting to be either at a repeating complex element or an array when parsing a repeating member.", _current); if (result == null) result = ReflectionHelper.CreateGenericList(prop.ElementType); var reader = new DispatchingReader(_current, arrayMode: true); result.Add(reader.Deserialize(prop, memberName)); return result; }
public void Serialize(PropertyMapping prop, object instance, bool summary, ComplexTypeWriter.SerializationMode mode) { if (prop == null) throw Error.ArgumentNull("prop"); var elements = instance as IList; if(elements == null) throw Error.Argument("existing", "Can only write repeating elements from a type implementing IList"); _current.WriteStartArray(); foreach(var element in elements) { var writer = new DispatchingWriter(_current); writer.Serialize(prop, element, summary, mode); } _current.WriteEndArray(); }
private void write(ClassMapping mapping, object instance, bool summary, PropertyMapping prop, SerializationMode mode) { // Check whether we are asked to just serialize the value element (Value members of primitive Fhir datatypes) // or only the other members (Extension, Id etc in primitive Fhir datatypes) // Default is all if (mode == SerializationMode.ValueElement && !prop.RepresentsValueElement) return; if (mode == SerializationMode.NonValueElements && prop.RepresentsValueElement) return; var value = prop.GetValue(instance); var isEmptyArray = (value as IList) != null && ((IList)value).Count == 0; // Message.Info("Handling member {0}.{1}", mapping.Name, prop.Name); if (value != null && !isEmptyArray) { string memberName = prop.Name; // For Choice properties, determine the actual name of the element // by appending its type to the base property name (i.e. deceasedBoolean, deceasedDate) if (prop.Choice == ChoiceType.DatatypeChoice) { memberName = determineElementMemberName(prop.Name, GetSerializationTypeForDataTypeChoiceElements(prop, value)); } _writer.WriteStartProperty(memberName); var writer = new DispatchingWriter(_writer); // Now, if our writer does not use dual properties for primitive values + rest (xml), // or this is a complex property without value element, serialize data normally if(!_writer.HasValueElementSupport || !serializedIntoTwoProperties(prop,value)) writer.Serialize(prop, value, summary, SerializationMode.AllMembers); else { // else split up between two properties, name and _name writer.Serialize(prop,value, summary, SerializationMode.ValueElement); _writer.WriteEndProperty(); _writer.WriteStartProperty("_" + memberName); writer.Serialize(prop, value, summary, SerializationMode.NonValueElements); } _writer.WriteEndProperty(); } }
private ClassMapping determineElementPropertyType(PropertyMapping mappedProperty, string memberName) { ClassMapping result = null; var typeName = mappedProperty.GetChoiceSuffixFromName(memberName); // Exception: valueResource actually means the element is of type ResourceReference if (typeName == "Resource") typeName = "ResourceReference"; // NB: this will return the latest type registered for that name, so supports type mapping/overriding // Maybe we should Import the types present on the choice, to make sure they are available. For now // assume the caller has Imported all types in the right (overriding) order. result = _inspector.FindClassMappingForFhirDataType(typeName); if (result == null) throw Error.Format("Encountered polymorph member {0}, which uses unknown datatype {1}", _current, memberName, typeName); return result; }
/// <summary> /// Enumerate this class' properties using reflection, create PropertyMappings /// for them and add them to the PropertyMappings. /// </summary> private static void inspectProperties(ClassMapping me) { foreach (var property in ReflectionHelper.FindPublicProperties(me.NativeType)) { // Skip properties that are marked as NotMapped if (ReflectionHelper.GetAttribute <NotMappedAttribute>(property) != null) { continue; } var propMapping = PropertyMapping.Create(property); me._propMappings.Add(propMapping.Name.ToUpperInvariant(), propMapping); // Keep a pointer to this property if this is a primitive value element ("Value" in primitive types) if (propMapping.RepresentsValueElement) { me.PrimitiveValueProperty = propMapping; } } me._orderedMappings = me._propMappings.Values.OrderBy(prop => prop.Order).ToList(); }
public PropertyMapping FindMappedElementByName(string name) { if (name == null) { throw Error.ArgumentNull("name"); } var normalizedName = name.ToUpperInvariant(); PropertyMapping prop = null; bool success = _propMappings.TryGetValue(normalizedName, out prop); // Direct success if (success) { return(prop); } // Not found, maybe a polymorphic name // TODO: specify possible polymorhpic variations using attributes // to speedup look up & aid validation return(PropertyMappings.SingleOrDefault(p => p.MatchesSuffixedName(name))); }
// If we have a normal complex property, for which the type has a primitive value member... private bool serializedIntoTwoProperties(PropertyMapping prop, object instance) { if (instance is IList) instance = ((IList)instance)[0]; if (!prop.IsPrimitive && prop.Choice != ChoiceType.ResourceChoice) { var mapping = _inspector.ImportType(instance.GetType()); return mapping.HasPrimitiveValueMember; } else return false; }
public static bool TryCreate(PropertyInfo prop, out PropertyMapping mapping, string version = null) => TryCreate(prop, out mapping, out var _, version);
internal static PropertyMapping Create(PropertyInfo prop, out IEnumerable <Type> referredTypes) { if (prop == null) { throw Error.ArgumentNull(nameof(prop)); } var foundTypes = new List <Type>(); PropertyMapping result = new PropertyMapping(); var elementAttr = prop.GetCustomAttribute <FhirElementAttribute>(); var cardinalityAttr = prop.GetCustomAttribute <Validation.CardinalityAttribute>(); var allowedTypes = prop.GetCustomAttribute <Validation.AllowedTypesAttribute>(); result.Name = determinePropertyName(prop); result.ImplementingType = prop.PropertyType; result.InSummary = elementAttr?.InSummary ?? false; result.IsMandatoryElement = cardinalityAttr != null ? cardinalityAttr.Min > 0 : false; result.Choice = elementAttr?.Choice ?? ChoiceType.None; if (elementAttr != null) { result.SerializationHint = elementAttr.XmlSerialization; result.Order = elementAttr.Order; } result.IsCollection = ReflectionHelper.IsTypedCollection(prop.PropertyType) && !prop.PropertyType.IsArray; // Get to the actual (native) type representing this element if (result.IsCollection) { result.ImplementingType = ReflectionHelper.GetCollectionItemType(prop.PropertyType); } if (ReflectionHelper.IsNullableType(result.ImplementingType)) { result.ImplementingType = ReflectionHelper.GetNullableArgument(result.ImplementingType); } result.IsPrimitive = isAllowedNativeTypeForDataTypeValue(result.ImplementingType); result.IsBackboneElement = result.ImplementingType.CanBeTreatedAsType(typeof(IBackboneElement)); foundTypes.Add(result.ImplementingType); // Derive the C# type that represents which types are allowed for this element. // This may differ from the ImplementingType in several ways: // * for a choice, ImplementingType = Any, but FhirType[] contains the possible choices // * some elements (e.g. Extension.url) have ImplementingType = string, but FhirType = FhirUri, etc. if (allowedTypes != null) { result.FhirType = allowedTypes.Types; } else if (elementAttr?.TypeRedirect != null) { result.FhirType = new[] { elementAttr.TypeRedirect } } ; else { result.FhirType = new[] { result.ImplementingType } }; // Check wether this property represents a native .NET type // marked to receive the class' primitive value in the fhir serialization // (e.g. the value from the Xml 'value' attribute or the Json primitive member value) if (result.IsPrimitive) { result.RepresentsValueElement = isPrimitiveValueElement(prop); } referredTypes = foundTypes; #if USE_CODE_GEN result._getter = prop.GetValueGetter(); result._setter = prop.GetValueSetter(); #else result._getter = instance => prop.GetValue(instance, null); result._setter = (instance, value) => prop.SetValue(instance, value, null); #endif return(result); }
private Type GetSerializationTypeForDataTypeChoiceElements( PropertyMapping prop, object value) { Type serializationType = value.GetType(); if (!prop.IsPrimitive && false) { #if PORTABLE45 Type baseType = serializationType.GetTypeInfo().BaseType; while (baseType != typeof(Element) && baseType != typeof(object)) { serializationType = baseType; baseType = baseType.GetTypeInfo().BaseType; } #else Type baseType = serializationType.BaseType; while (baseType != typeof(Element) && baseType != typeof(object)) { serializationType = baseType; baseType = baseType.BaseType; } #endif } return serializationType; }