예제 #1
0
        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);
        }
예제 #2
0
        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;
        }
예제 #4
0
        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;
        }
예제 #5
0
        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);
        }
예제 #6
0
        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();
        }
예제 #10
0
        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();
            }
        }
예제 #11
0
        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;
        }
예제 #12
0
        /// <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();
        }
예제 #13
0
        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)));
        }
예제 #14
0
        // 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;
        }
예제 #15
0
 public static bool TryCreate(PropertyInfo prop, out PropertyMapping mapping, string version = null) => TryCreate(prop, out mapping, out var _, version);
예제 #16
0
        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);
        }
예제 #17
0
        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;
        }