diff --git a/src/Benchmarks/Benchmarks.csproj b/src/Benchmarks/Benchmarks.csproj
index d9a65e755b..118d35af9a 100644
--- a/src/Benchmarks/Benchmarks.csproj
+++ b/src/Benchmarks/Benchmarks.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Hl7.Fhir.Base/Introspection/DeclaredTypeAttribute.cs b/src/Hl7.Fhir.Base/Introspection/DeclaredTypeAttribute.cs
index ccc3aaebe5..3fa1c0a6ad 100644
--- a/src/Hl7.Fhir.Base/Introspection/DeclaredTypeAttribute.cs
+++ b/src/Hl7.Fhir.Base/Introspection/DeclaredTypeAttribute.cs
@@ -16,7 +16,7 @@ namespace Hl7.Fhir.Introspection
/// in the constructor to this attribute.
///
[CLSCompliant(false)]
- [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
+ [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class DeclaredTypeAttribute : VersionedAttribute
{
public DeclaredTypeAttribute()
diff --git a/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs b/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs
index 7a49a53e15..78793fc5f3 100644
--- a/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs
+++ b/src/Hl7.Fhir.Base/Introspection/ModelInspector.cs
@@ -22,7 +22,7 @@ namespace Hl7.Fhir.Introspection
///
/// A cache of FHIR type mappings found on .NET classes.
///
- /// POCO's in the "common" assemblies
+ /// POCO's in the "base" assembly
/// can reflect the definition of multiple releases of FHIR using
/// attributes. A will always capture the metadata for one such
/// which is passed to it in the constructor.
@@ -41,8 +41,8 @@ public class ModelInspector : IStructureDefinitionSummaryProvider, IModelInfo
/// Calling this function repeatedly for the same type will return the same ClassMapping.
///
/// If the type given is FHIR Release specific, the returned mapping will contain
- /// metadata for that release only. If the type is from the common assembly, it will contain
- /// metadata for that type from the most recent release of the common assembly.
+ /// metadata for that release only. If the type is from the base assembly, it will contain
+ /// metadata for that type from the most recent release of the base assembly.
public static ClassMapping? GetClassMappingForType(Type t) =>
ForAssembly(t.GetTypeInfo().Assembly).FindOrImportClassMapping(t);
@@ -52,8 +52,8 @@ public class ModelInspector : IStructureDefinitionSummaryProvider, IModelInfo
/// the same assembly will return the same inspector.
///
/// If the assembly given is FHIR Release specific, the returned inspector will contain
- /// metadata for that release only. If the assembly is the common assembly, it will contain
- /// metadata for the most recent release for those common classes.
+ /// metadata for that release only. If the assembly is the base assembly, it will contain
+ /// metadata for the most recent release for those base classes.
public static ModelInspector ForAssembly(Assembly a)
{
return _inspectedAssemblies.GetOrAdd(a.FullName ?? throw Error.ArgumentNull(nameof(a.FullName)), _ => configureInspector(a));
@@ -67,10 +67,10 @@ static ModelInspector configureInspector(Assembly a)
var newInspector = new ModelInspector(modelAssemblyAttr.Since);
newInspector.Import(a);
- // Make sure we always include the common types too.
- var commonAssembly = typeof(Resource).GetTypeInfo().Assembly;
- if (a.FullName != commonAssembly.FullName)
- newInspector.Import(commonAssembly);
+ // Make sure we always include the types from the base assembly too.
+ var baseAssembly = typeof(Resource).GetTypeInfo().Assembly;
+ if (a.FullName != baseAssembly.FullName)
+ newInspector.Import(baseAssembly);
// And finally, the System/CQL primitive types
foreach (var cqlType in getCqlTypes())
@@ -104,9 +104,9 @@ static IEnumerable getCqlTypes()
///
/// Returns a fully configured with the
- /// FHIR metadata contents of the common assembly
+ /// FHIR metadata contents of the base assembly
///
- public static ModelInspector Common => ForType(typeof(ModelInspector));
+ public static ModelInspector Base => ForType(typeof(ModelInspector));
///
/// Constructs a ModelInspector that will reflect the FHIR metadata for the given FHIR release
diff --git a/src/Hl7.Fhir.Base/Model/Generated/Attachment.cs b/src/Hl7.Fhir.Base/Model/Generated/Attachment.cs
index 92b05fe6f3..817624abbc 100644
--- a/src/Hl7.Fhir.Base/Model/Generated/Attachment.cs
+++ b/src/Hl7.Fhir.Base/Model/Generated/Attachment.cs
@@ -183,6 +183,8 @@ public string Url
/// Number of bytes of content (if url provided)
///
[FhirElement("size", InSummary=true, Order=70)]
+ [DeclaredType(Type = typeof(UnsignedInt), Since = FhirRelease.STU3)]
+ [DeclaredType(Type = typeof(Integer64), Since = FhirRelease.R5)]
[DataMember]
public Hl7.Fhir.Model.Integer64 SizeElement
{
diff --git a/src/Hl7.Fhir.Base/Model/Markdown.cs b/src/Hl7.Fhir.Base/Model/Markdown.cs
index a6f6755e5a..b44b8de229 100644
--- a/src/Hl7.Fhir.Base/Model/Markdown.cs
+++ b/src/Hl7.Fhir.Base/Model/Markdown.cs
@@ -40,8 +40,8 @@ public partial class Markdown
///
public static bool IsValidValue(string value) => FhirString.IsValidValue(value);
- public static implicit operator string(Markdown md) => md.Value;
- public static implicit operator Markdown(string s) => new(s);
+ public static implicit operator string?(Markdown? md) => md?.Value;
+ public static implicit operator Markdown?(string? s) => s is not null ? new(s) : null;
}
diff --git a/src/Hl7.Fhir.Base/Properties/AssemblyInfo.cs b/src/Hl7.Fhir.Base/Properties/AssemblyInfo.cs
index d8deb1b4f9..744c396e65 100644
--- a/src/Hl7.Fhir.Base/Properties/AssemblyInfo.cs
+++ b/src/Hl7.Fhir.Base/Properties/AssemblyInfo.cs
@@ -2,7 +2,7 @@
using System;
using System.Runtime.CompilerServices;
-[assembly: FhirModelAssembly]
+[assembly: FhirModelAssembly(Since = Hl7.Fhir.Specification.FhirRelease.STU3)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
diff --git a/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs b/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs
index a3a78088a7..3f6f3a2045 100644
--- a/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs
+++ b/src/Hl7.Fhir.Base/Rest/TransactionBuilder.cs
@@ -269,7 +269,7 @@ private string paramValueToString(Parameters.ParameterComponent parameter)
case CodeableConcept codeableConcept:
return codeableConcept.ToToken();
default:
- if (ModelInspector.Common.IsPrimitive(parameter.Value.GetType()))
+ if (ModelInspector.Base.IsPrimitive(parameter.Value.GetType()))
{
return parameter.Value.ToString();
}
diff --git a/src/Hl7.Fhir.Base/Serialization/FhirJsonBuilder.cs b/src/Hl7.Fhir.Base/Serialization/FhirJsonBuilder.cs
index 6d3b0a27b0..c5ac6003fc 100644
--- a/src/Hl7.Fhir.Base/Serialization/FhirJsonBuilder.cs
+++ b/src/Hl7.Fhir.Base/Serialization/FhirJsonBuilder.cs
@@ -103,7 +103,7 @@ private JObject buildInternal(ITypedElement source)
object value = node.Definition != null ? node.Value : details?.OriginalValue ?? node.Value;
var objectInShadow = node.InstanceType != null ? primitiveTypes.Contains(node.InstanceType) : details?.UsesShadow ?? false;
- JToken first = value != null ? buildValue(value) : null;
+ JToken first = value != null ? buildValue(value, node.InstanceType) : null;
JObject second = buildChildren(node);
// If this is a complex type with a value (should not occur)
@@ -200,25 +200,12 @@ private void addChildren(ITypedElement node, JObject parent)
}
}
- private JValue buildValue(object value)
+ private JValue buildValue(object value, string requiredType = null) => value switch
{
- switch (value)
- {
- case bool b:
- case decimal d:
- case Int32 i32:
- case Int16 i16:
- case ulong ul:
- case double db:
- case BigInteger bi:
- case float f:
- return new JValue(value);
- case string s:
- return new JValue(s.Trim());
- case long l:
- default:
- return new JValue(PrimitiveTypeConverter.ConvertTo(value));
- }
- }
+ bool or decimal or Int32 or Int16 or ulong or double or BigInteger or float => new JValue(value),
+ string s => new JValue(s.Trim()),
+ long l when requiredType is "integer" or "unsignedInt" or "positiveInt" => new JValue(l),
+ _ => new JValue(PrimitiveTypeConverter.ConvertTo(value)),
+ };
}
}
diff --git a/src/Hl7.Fhir.Base/Serialization/FhirJsonException.cs b/src/Hl7.Fhir.Base/Serialization/FhirJsonException.cs
index 17ccdae6fe..e6401629d2 100644
--- a/src/Hl7.Fhir.Base/Serialization/FhirJsonException.cs
+++ b/src/Hl7.Fhir.Base/Serialization/FhirJsonException.cs
@@ -11,7 +11,6 @@
using Hl7.Fhir.Utility;
using System;
using System.Globalization;
-using System.Linq;
using System.Text.Json;
#nullable enable
@@ -46,6 +45,8 @@ public class FhirJsonException : CodedException
public const string RESOURCETYPE_UNEXPECTED_CODE = "JSON119";
public const string OBJECTS_CANNOT_BE_EMPTY_CODE = "JSON120";
public const string ARRAYS_CANNOT_BE_EMPTY_CODE = "JSON121";
+ public const string LONG_CANNOT_BE_PARSED_CODE = "JSON122";
+ public const string LONG_INCORRECT_FORMAT_CODE = "JSON123";
[Obsolete("According to the latest updates of the Json format, primitive arrays of different sizes are no longer considered an error.")]
public const string PRIMITIVE_ARRAYS_INCOMPAT_SIZE_CODE = "JSON122";
@@ -81,6 +82,10 @@ public class FhirJsonException : CodedException
internal static readonly FhirJsonException NUMBER_CANNOT_BE_PARSED = new(NUMBER_CANNOT_BE_PARSED_CODE, "Json number '{0}' cannot be parsed as a {1}.");
internal static readonly FhirJsonException UNEXPECTED_JSON_TOKEN = new(UNEXPECTED_JSON_TOKEN_CODE, "Expecting a {0}, but found a json {1} with value '{2}'.");
+ // In R5 Integer64 (long) are serialized as string. So we would expect a string during parsing.
+ internal static readonly FhirJsonException LONG_CANNOT_BE_PARSED = new(LONG_CANNOT_BE_PARSED_CODE, "Json string '{0}' cannot be parsed as a {1}.");
+ internal static readonly FhirJsonException LONG_INCORRECT_FORMAT = new(LONG_INCORRECT_FORMAT_CODE, "{0} '{1}' cannot be parsed as a {2}, because it should be a {3}.");
+
// The parser will turn a non-array value into an array with a single element, so no data is lost.
internal static readonly FhirJsonException EXPECTED_START_OF_ARRAY = new(EXPECTED_START_OF_ARRAY_CODE, "Expected start of array.");
diff --git a/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoDeserializer.cs b/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoDeserializer.cs
index c998c8c839..81676c04f6 100644
--- a/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoDeserializer.cs
+++ b/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoDeserializer.cs
@@ -13,6 +13,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Text.Json;
using ERR = Hl7.Fhir.Serialization.FhirJsonException;
@@ -344,11 +345,13 @@ FhirJsonPocoDeserializerState state
// (one with, and one without the '_')
var existingValue = propertyMapping.GetValue(target);
+ var fhirType = propertyMapping.FhirType.FirstOrDefault();
+
// Note that the POCO model will always allocate a new list if the property had not been set before,
// so there is always an existingValue for IList
result = propertyMapping.IsCollection ?
- deserializeFhirPrimitiveList((IList)existingValue!, propertyName, propertyValueMapping, ref reader, delayedValidations, state) :
- DeserializeFhirPrimitive(existingValue as PrimitiveType, propertyName, propertyValueMapping, ref reader, delayedValidations, state);
+ deserializeFhirPrimitiveList((IList)existingValue!, propertyName, propertyValueMapping, fhirType, ref reader, delayedValidations, state) :
+ DeserializeFhirPrimitive(existingValue as PrimitiveType, propertyName, propertyValueMapping, fhirType, ref reader, delayedValidations, state);
}
else
{
@@ -358,8 +361,8 @@ FhirJsonPocoDeserializerState state
// Note that repeating simple elements (like Extension.url) do not currently exist in the FHIR serialization
result = propertyMapping.IsCollection
- ? deserializeNormalList(propertyValueMapping, ref reader, state)
- : deserializeSingleValue(ref reader, propertyValueMapping, state);
+ ? deserializeNormalList(propertyValueMapping, ref reader, propertyMapping, state)
+ : deserializeSingleValue(ref reader, propertyValueMapping, propertyMapping, state);
}
// Only do validation when no parse errors were encountered, otherwise we'll just
@@ -397,6 +400,7 @@ FhirJsonPocoDeserializerState state
private IList? deserializeNormalList(
ClassMapping propertyValueMapping,
ref Utf8JsonReader reader,
+ PropertyMapping propertyMapping,
FhirJsonPocoDeserializerState state)
{
// Create a list of the type of this property's value.
@@ -424,7 +428,7 @@ FhirJsonPocoDeserializerState state
// to simply create a list by Adding(). Not the fastest approach :-(
while (reader.TokenType != JsonTokenType.EndArray)
{
- var result = deserializeSingleValue(ref reader, propertyValueMapping, state);
+ var result = deserializeSingleValue(ref reader, propertyValueMapping, propertyMapping, state);
listInstance.Add(result);
if (oneshot) break;
@@ -462,6 +466,7 @@ public void Run()
IList existingList,
string propertyName,
ClassMapping propertyValueMapping,
+ Type? fhirType,
ref Utf8JsonReader reader,
DelayedValidations delayedValidations,
FhirJsonPocoDeserializerState state
@@ -508,7 +513,7 @@ FhirJsonPocoDeserializerState state
{
existingList[elementIndex] ??= propertyValueMapping.Factory();
onlyNulls = false;
- _ = DeserializeFhirPrimitive((PrimitiveType)existingList[elementIndex]!, propertyName, propertyValueMapping, ref reader, delayedValidations, state);
+ _ = DeserializeFhirPrimitive((PrimitiveType)existingList[elementIndex]!, propertyName, propertyValueMapping, fhirType, ref reader, delayedValidations, state);
}
elementIndex += 1;
@@ -538,6 +543,7 @@ internal PrimitiveType DeserializeFhirPrimitive(
PrimitiveType? existingPrimitive,
string propertyName,
ClassMapping propertyValueMapping,
+ Type? fhirType,
ref Utf8JsonReader reader,
DelayedValidations? delayedValidations,
FhirJsonPocoDeserializerState state
@@ -558,7 +564,7 @@ FhirJsonPocoDeserializerState state
try
{
- var (result, error) = DeserializePrimitiveValue(ref reader, primitiveValueProperty.ImplementingType);
+ var (result, error) = DeserializePrimitiveValue(ref reader, primitiveValueProperty.ImplementingType, fhirType);
// Only do validation when no parse errors were encountered, otherwise we'll just
// produce spurious messages.
@@ -609,7 +615,7 @@ FhirJsonPocoDeserializerState state
/// Deserializes a single object, either a resource, a FHIR primitive or a primitive value.
///
/// Upon completion, reader will be located at the next token afther the value.
- private object? deserializeSingleValue(ref Utf8JsonReader reader, ClassMapping propertyValueMapping, FhirJsonPocoDeserializerState state)
+ private object? deserializeSingleValue(ref Utf8JsonReader reader, ClassMapping propertyValueMapping, PropertyMapping propertyMapping, FhirJsonPocoDeserializerState state)
{
// Resources
if (propertyValueMapping.IsResource)
@@ -622,7 +628,7 @@ FhirJsonPocoDeserializerState state
// needs to handle PrimitiveType.ObjectValue & dual properties.
else if (propertyValueMapping.IsPrimitive)
{
- var (result, error) = DeserializePrimitiveValue(ref reader, propertyValueMapping.NativeType);
+ var (result, error) = DeserializePrimitiveValue(ref reader, propertyValueMapping.NativeType, propertyMapping.FhirType.FirstOrDefault());
if (error is not null && result is not null)
{
@@ -654,7 +660,7 @@ FhirJsonPocoDeserializerState state
/// A value without an error if the data could be parsed to the required type, and a value with an error if the
/// value could not be parsed - in which case the value returned is the raw value coming in from the reader.
/// Upon completion, the reader will be positioned on the token after the primitive.
- internal (object?, FhirJsonException?) DeserializePrimitiveValue(ref Utf8JsonReader reader, Type requiredType)
+ internal (object?, FhirJsonException?) DeserializePrimitiveValue(ref Utf8JsonReader reader, Type implementingType, Type? fhirType)
{
// Check for unexpected non-value types.
if (reader.TokenType is JsonTokenType.StartObject or JsonTokenType.StartArray)
@@ -670,16 +676,17 @@ FhirJsonPocoDeserializerState state
(object? partial, FhirJsonException? error) result = reader.TokenType switch
{
JsonTokenType.Null => new(null, ERR.EXPECTED_PRIMITIVE_NOT_NULL.With(ref reader)),
- JsonTokenType.String when requiredType == typeof(string) => new(reader.GetString(), null),
- JsonTokenType.String when requiredType == typeof(byte[]) =>
+ JsonTokenType.String when implementingType == typeof(string) => new(reader.GetString(), null),
+ JsonTokenType.String when implementingType == typeof(byte[]) =>
!Settings.DisableBase64Decoding ? readBase64(ref reader) : new(reader.GetString(), null),
- JsonTokenType.String when requiredType == typeof(DateTimeOffset) => readDateTimeOffset(ref reader),
- JsonTokenType.String when requiredType.IsEnum => new(reader.GetString(), null),
+ JsonTokenType.String when implementingType == typeof(DateTimeOffset) => readDateTimeOffset(ref reader),
+ JsonTokenType.String when implementingType.IsEnum => new(reader.GetString(), null),
+ JsonTokenType.String when implementingType == typeof(long) => readLong(ref reader, fhirType),
//JsonTokenType.String when requiredType.IsEnum => readEnum(ref reader, requiredType),
- JsonTokenType.String => unexpectedToken(ref reader, reader.GetString(), requiredType.Name, "string"),
- JsonTokenType.Number => tryGetMatchingNumber(ref reader, requiredType),
- JsonTokenType.True or JsonTokenType.False when requiredType == typeof(bool) => new(reader.GetBoolean(), null),
- JsonTokenType.True or JsonTokenType.False => unexpectedToken(ref reader, reader.GetRawText(), requiredType.Name, "boolean"),
+ JsonTokenType.String => unexpectedToken(ref reader, reader.GetString(), implementingType.Name, "string"),
+ JsonTokenType.Number => tryGetMatchingNumber(ref reader, implementingType, fhirType),
+ JsonTokenType.True or JsonTokenType.False when implementingType == typeof(bool) => new(reader.GetBoolean(), null),
+ JsonTokenType.True or JsonTokenType.False => unexpectedToken(ref reader, reader.GetRawText(), implementingType.Name, "boolean"),
_ =>
// This would be an internal logic error, since our callers should have made sure we're
@@ -692,7 +699,7 @@ FhirJsonPocoDeserializerState state
// If there is a failure, and we have a handler installed, call it
if (Settings.OnPrimitiveParseFailed is not null && result.error is not null)
- result = Settings.OnPrimitiveParseFailed(ref reader, requiredType, result.partial, result.error);
+ result = Settings.OnPrimitiveParseFailed(ref reader, implementingType, result.partial, result.error);
// Read past the value
reader.Read();
@@ -713,6 +720,26 @@ FhirJsonPocoDeserializerState state
new(contents, ERR.STRING_ISNOTAN_INSTANT.With(ref reader, contents));
}
+ static (object?, FhirJsonException?) readLong(ref Utf8JsonReader reader, Type? fhirType)
+ {
+ // convert string in json to a long.
+ var contents = reader.GetString()!;
+
+ return long.TryParse(contents, out var parsed) switch
+ {
+ true when isInteger64() => new(parsed, null),
+ true => new(parsed, ERR.LONG_INCORRECT_FORMAT.With(ref reader, "Json string", contents, typeName(), "Json number")),
+ false when isInteger64() => new(contents, ERR.LONG_CANNOT_BE_PARSED.With(ref reader, contents, nameof(Integer64))),
+ false => new(contents, ERR.LONG_INCORRECT_FORMAT.With(ref reader, "Json string", contents, typeName(), "Json number"))
+ };
+
+ string typeName()
+ => fhirType?.Name ?? string.Empty;
+
+ bool isInteger64()
+ => fhirType == typeof(Integer64);
+ }
+
// Validation is now done using POCO validation, so have removed it here.
// Keep code around in case I make my mind up before publication.
//static (object?, FhirJsonException?) readEnum(ref Utf8JsonReader reader, Type enumType)
@@ -733,7 +760,7 @@ private static (object?, FhirJsonException) unexpectedToken(ref Utf8JsonReader r
/// This function tries to map from the json-format "generic" number to the kind of numeric type defined in the POCO.
///
/// Reader must be positioned on a number token. This function will not move the reader to the next token.
- private static (object?, FhirJsonException?) tryGetMatchingNumber(ref Utf8JsonReader reader, Type requiredType)
+ private static (object?, FhirJsonException?) tryGetMatchingNumber(ref Utf8JsonReader reader, Type implementingType, Type? fhirType)
{
if (reader.TokenType != JsonTokenType.Number)
throw new InvalidOperationException($"Cannot read a numeric when reader is on a {reader.TokenType}. " +
@@ -742,35 +769,37 @@ private static (object?, FhirJsonException?) tryGetMatchingNumber(ref Utf8JsonRe
object? value = null;
bool success;
- if (requiredType == typeof(decimal))
+ if (implementingType == typeof(decimal))
success = reader.TryGetDecimal(out decimal dec) && (value = dec) is { };
- else if (requiredType == typeof(int))
+ else if (implementingType == typeof(int))
success = reader.TryGetInt32(out int i32) && (value = i32) is { };
- else if (requiredType == typeof(uint))
+ else if (implementingType == typeof(uint))
success = reader.TryGetUInt32(out uint ui32) && (value = ui32) is { };
- else if (requiredType == typeof(long))
+ else if (implementingType == typeof(long))
success = reader.TryGetInt64(out long i64) && (value = i64) is { };
- else if (requiredType == typeof(ulong))
+ else if (implementingType == typeof(ulong))
success = reader.TryGetUInt64(out ulong ui64) && (value = ui64) is { };
- else if (requiredType == typeof(float))
+ else if (implementingType == typeof(float))
success = reader.TryGetSingle(out float si) && si.IsNormal() && (value = si) is { };
- else if (requiredType == typeof(double))
+ else if (implementingType == typeof(double))
success = reader.TryGetDouble(out double dbl) && dbl.IsNormal() && (value = dbl) is { };
else
{
var rawValue = reader.GetRawText();
- return unexpectedToken(ref reader, rawValue, requiredType.Name, "number");
+ return unexpectedToken(ref reader, rawValue, implementingType.Name, "number");
}
// We expected a number, we found a json number, but they don't match (e.g. precision etc)
if (success)
{
- return new(value, null);
+ return implementingType == typeof(long) && fhirType == typeof(Integer64)
+ ? new(value, ERR.LONG_INCORRECT_FORMAT.With(ref reader, "Json number", reader.GetRawText(), nameof(Integer64), "Json string"))
+ : new(value, null);
}
else
{
var rawValue = reader.GetRawText();
- return new(rawValue, ERR.NUMBER_CANNOT_BE_PARSED.With(ref reader, rawValue, requiredType.Name));
+ return new(rawValue, ERR.NUMBER_CANNOT_BE_PARSED.With(ref reader, rawValue, implementingType.Name));
}
}
diff --git a/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoSerializer.cs b/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoSerializer.cs
index f56d28d40d..445aecb302 100644
--- a/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoSerializer.cs
+++ b/src/Hl7.Fhir.Base/Serialization/FhirJsonPocoSerializer.cs
@@ -19,7 +19,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
-using System.Threading;
namespace Hl7.Fhir.Serialization
{
@@ -98,11 +97,12 @@ private void serializeInternal(
var propertyName = propertyMapping?.Choice == ChoiceType.DatatypeChoice ?
addSuffixToElementName(member.Key, member.Value) : member.Key;
+ var requiredType = propertyMapping?.FhirType.FirstOrDefault();
if (member.Value is PrimitiveType pt)
- serializeFhirPrimitive(propertyName, pt, writer);
+ serializeFhirPrimitive(propertyName, pt, writer, requiredType);
else if (member.Value is IReadOnlyCollection pts)
- serializeFhirPrimitiveList(propertyName, pts, writer);
+ serializeFhirPrimitiveList(propertyName, pts, writer, requiredType);
else
{
writer.WritePropertyName(propertyName);
@@ -112,12 +112,12 @@ private void serializeInternal(
writer.WriteStartArray();
foreach (var value in coll)
- serializeMemberValue(value, writer);
+ serializeMemberValue(value, writer, requiredType);
writer.WriteEndArray();
}
else
- serializeMemberValue(member.Value, writer);
+ serializeMemberValue(member.Value, writer, requiredType);
}
filter?.LeaveMember(member.Key, member.Value, propertyMapping);
@@ -140,12 +140,12 @@ private static string addSuffixToElementName(string elementName, object elementV
}
- private void serializeMemberValue(object value, Utf8JsonWriter writer)
+ private void serializeMemberValue(object value, Utf8JsonWriter writer, Type? requiredType = null)
{
if (value is IReadOnlyDictionary complex)
serializeInternal(complex, writer, skipValue: false);
else
- SerializePrimitiveValue(value, writer);
+ SerializePrimitiveValue(value, writer, requiredType);
}
///
@@ -157,7 +157,8 @@ private void serializeMemberValue(object value, Utf8JsonWriter writer)
private void serializeFhirPrimitiveList(
string elementName,
IReadOnlyCollection values,
- Utf8JsonWriter writer)
+ Utf8JsonWriter writer,
+ Type? requiredType = null)
{
if (values is null) throw new ArgumentNullException(nameof(values));
@@ -181,7 +182,7 @@ private void serializeFhirPrimitiveList(
writeStartArray(elementName, numNullsMissed, writer);
}
- SerializePrimitiveValue(value!.ObjectValue, writer);
+ SerializePrimitiveValue(value!.ObjectValue, writer, requiredType);
}
else
{
@@ -239,7 +240,7 @@ private static void writeStartArray(string propName, int numNulls, Utf8JsonWrite
///
/// FHIR primitives are handled separately here since they may require
/// serialization into two Json properties called "elementName" and "_elementName".
- private void serializeFhirPrimitive(string elementName, PrimitiveType value, Utf8JsonWriter writer)
+ private void serializeFhirPrimitive(string elementName, PrimitiveType value, Utf8JsonWriter writer, Type? requiredType = null)
{
if (value is null) throw new ArgumentNullException(nameof(value));
@@ -247,7 +248,7 @@ private void serializeFhirPrimitive(string elementName, PrimitiveType value, Utf
{
// Write a property with 'elementName'
writer.WritePropertyName(elementName);
- SerializePrimitiveValue(value.ObjectValue, writer);
+ SerializePrimitiveValue(value.ObjectValue, writer, requiredType);
}
if (value.HasElements)
@@ -271,13 +272,22 @@ private void serializeFhirPrimitive(string elementName, PrimitiveType value, Utf
/// to be written that fit in .NET's type, which may be less
/// precision than required by the FHIR specification (http://hl7.org/fhir/json.html#primitive).
///
- protected virtual void SerializePrimitiveValue(object value, Utf8JsonWriter writer)
+ protected virtual void SerializePrimitiveValue(object value, Utf8JsonWriter writer, Type? requiredType)
{
switch (value)
{
case int i32: writer.WriteNumberValue(i32); break;
case uint ui32: writer.WriteNumberValue(ui32); break;
- case long i64: writer.WriteNumberValue(i64); break;
+ case long i64:
+ {
+ // in case of Integer64, then the value must be serialized as a string due to
+ // issues with precision in floating point libraries.
+ if (requiredType == typeof(Integer64))
+ writer.WriteStringValue(i64.ToString());
+ else
+ writer.WriteNumberValue(i64);
+ break;
+ }
case ulong ui64: writer.WriteNumberValue(ui64); break;
case float si: writer.WriteNumberValue(si); break;
case double dbl: writer.WriteNumberValue(dbl); break;
diff --git a/src/Hl7.Fhir.Conformance/Properties/AssemblyInfo.cs b/src/Hl7.Fhir.Conformance/Properties/AssemblyInfo.cs
index b025c81117..7715f0bfb5 100644
--- a/src/Hl7.Fhir.Conformance/Properties/AssemblyInfo.cs
+++ b/src/Hl7.Fhir.Conformance/Properties/AssemblyInfo.cs
@@ -5,7 +5,7 @@
// This assembly is not fully CLSCompliant, but this triggers compiler warnings to avoid the issue as described here, when mixing C# and VB
// https://msdn.microsoft.com/en-us/library/ms235408(v=vs.90).aspx
[assembly: CLSCompliant(true)]
-[assembly: FhirModelAssembly]
+[assembly: FhirModelAssembly(Since = Hl7.Fhir.Specification.FhirRelease.R4)]
#if DEBUG
[assembly: InternalsVisibleTo("Hl7.Fhir.R4")]
diff --git a/src/Hl7.Fhir.Conformance/Specification/Navigation/ElementDefinitionNavigationFunctions.cs b/src/Hl7.Fhir.Conformance/Specification/Navigation/ElementDefinitionNavigationFunctions.cs
index 5c0cd96f59..6a198b142b 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Navigation/ElementDefinitionNavigationFunctions.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Navigation/ElementDefinitionNavigationFunctions.cs
@@ -118,7 +118,7 @@ public static string ParseTypeFromRenamedElement(string rename, string choiceNam
// Primitive types start with a lower-case character
var altTypeName = Utility.StringExtensions.Uncapitalize(typeName);
- if (ModelInspector.Common.IsPrimitive(altTypeName)) { return altTypeName; }
+ if (ModelInspector.Base.IsPrimitive(altTypeName)) { return altTypeName; }
return typeName;
}
diff --git a/src/Hl7.Fhir.Conformance/Specification/Snapshot/ElementDefnMerger.cs b/src/Hl7.Fhir.Conformance/Specification/Snapshot/ElementDefnMerger.cs
index 0b1d440a5a..077e67f33d 100644
--- a/src/Hl7.Fhir.Conformance/Specification/Snapshot/ElementDefnMerger.cs
+++ b/src/Hl7.Fhir.Conformance/Specification/Snapshot/ElementDefnMerger.cs
@@ -173,7 +173,7 @@ void merge(ElementDefinition snap, ElementDefinition diff, bool mergeElementId,
snap.Binding = mergeBinding(snap.Binding, diff.Binding);
// [MV 20220803] Remove Binding when the element has no bindable type
- if (snap.Binding is not null && !snap.Type.Any(t => ModelInspector.Common.IsBindable(t.Code)))
+ if (snap.Binding is not null && !snap.Type.Any(t => ModelInspector.Base.IsBindable(t.Code)))
{
snap.Binding = null;
}
diff --git a/src/Hl7.Fhir.ElementModel.STU3.Tests/TypedElementToSourceNodeAdapterTests.cs b/src/Hl7.Fhir.ElementModel.STU3.Tests/TypedElementToSourceNodeAdapterTests.cs
index 650dd02b62..4976ecde33 100644
--- a/src/Hl7.Fhir.ElementModel.STU3.Tests/TypedElementToSourceNodeAdapterTests.cs
+++ b/src/Hl7.Fhir.ElementModel.STU3.Tests/TypedElementToSourceNodeAdapterTests.cs
@@ -28,7 +28,7 @@ public void AnnotationsTest()
var result2 = sourceNode.Annotation();
Assert.IsNotNull(result2);
Assert.AreEqual("TypedElementToSourceNodeAdapter", result2.GetType().Name); // I use the classname here, because PocoElementNode is internal in Hl7.Fhir.Core
- Assert.AreSame