Skip to content

Commit

Permalink
Fixed unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ewoutkramer committed Nov 25, 2024
1 parent 4d6fe69 commit 4290945
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 147 deletions.
11 changes: 9 additions & 2 deletions src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,16 @@ private Base readFromElement(ITypedElement node, ClassMapping classMapping)
var objectValue = newInstance is DynamicPrimitive ?
value :
convertTypedElementValue(value, node.InstanceType);
newInstance["value"] = objectValue;

if (settings?.AllowUnrecognizedEnums == false && classMapping.EnumType is not null && objectValue is string enumLiteral)
if(newInstance is PrimitiveType pt)
pt.ObjectValue = objectValue;
else
raiseFormatError($"{node.Name} is a primitive of type {value.GetType()}, but the target POCO is a {newInstance.GetType()}, " +
$"which is not FHIR primitive.", node.Location);

if (settings?.AllowUnrecognizedEnums == false &&
classMapping.EnumType is not null &&
objectValue is string enumLiteral)
{
// Backwards-compatible check for enums. Although our POCOs accept strings rather
// than enum values, this check is still useful for catching typos in the data and may
Expand Down
6 changes: 3 additions & 3 deletions src/Hl7.Fhir.Base/ElementModel/PocoElementNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal PocoElementNode(ModelInspector inspector, Base root, string rootName =
InstanceType = ((IStructureDefinitionSummary)_myClassMapping).TypeName;
Definition = ElementDefinitionSummary.ForRoot(_myClassMapping, rootName ?? root.TypeName);

Location = InstanceType;
Location = InstanceType!;
ShortPath = InstanceType;
}

Expand All @@ -49,9 +49,9 @@ private PocoElementNode(ModelInspector inspector, Base instance, PocoElementNode
var instanceType = definition.Choice != ChoiceType.None
? instance.GetType()
: determineInstanceType(definition);
_myClassMapping = _inspector.FindOrImportClassMapping(instanceType);
_myClassMapping = _inspector.FindOrImportClassMapping(instanceType)!;
InstanceType = ((IStructureDefinitionSummary)_myClassMapping).TypeName;
Definition = definition ?? throw Error.ArgumentNull(nameof(definition));
Definition = definition;

ExceptionHandler = parent.ExceptionHandler;
Location = location;
Expand Down
3 changes: 2 additions & 1 deletion src/Hl7.Fhir.Base/Introspection/PropertyMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ private ITypeSerializationInfo[] buildTypes()
}
else if (IsPrimitive)
{
throw new NotSupportedException($"Encountered unexpected primitive type {Name} for ITypedElement.InstanceType.")
throw new NotSupportedException(
$"Encountered unexpected primitive type {Name} for ITypedElement.InstanceType.");
}
else
{
Expand Down
6 changes: 5 additions & 1 deletion src/Hl7.Fhir.Base/Model/Base.Dictionary.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#if NOT_USED

using System;
using System.Collections;
using System.Collections.Generic;
Expand Down Expand Up @@ -46,4 +48,6 @@ IEnumerator IEnumerable.GetEnumerator() =>

#endregion

}
}

#endif
7 changes: 2 additions & 5 deletions src/Hl7.Fhir.Base/Model/Base.Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ public static IEnumerable<Base> Children(this Base instance)
case ("div", XHtml xhtml):
yield return new FhirString(xhtml.Value);
break;
case ("id", string id):
yield return new FhirString(id);
break;
case ("url", string url):
yield return new FhirUri(url);
case ("id", FhirUri id):
yield return new FhirString(id.Value);
break;
case (_, IEnumerable<Base> list):
foreach (var item in list)
Expand Down
26 changes: 10 additions & 16 deletions src/Hl7.Fhir.Base/Model/Base.TypedElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,13 @@ IEnumerable<ITypedElement> ITypedElement.Children(string? name) =>
this.GetElementPairs()
.Where(ep => (name == null || name == ep.Key))
.SelectMany<KeyValuePair<string, object>, Base>(ep =>
(ep.Key, ep.Value) switch
ep.Value switch
{
(_, Base b) => (IEnumerable<Base>) [b.WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
(_, IEnumerable<Base> list) => list.Select((item, idx) =>
Base b => (IEnumerable<Base>) [b.WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
IEnumerable<Base> list => list.Select((item, idx) =>
item.WithScopeInfo(new ScopeInformation(this, ep.Key, idx))),
("url", string s) when this is Extension =>
[new FhirUri(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
("id", string s) when this is Element =>
[new FhirString(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
("value", _) => [],
_ => throw new InvalidOperationException("Unexpected system primitive in child list")
_ => throw new InvalidOperationException($"Key '{ep.Key}' has a value with " +
$"unexpected type '{ep.Value.GetType()}'.")
}
);

Expand Down Expand Up @@ -218,14 +214,12 @@ bool IScopedNode.TryResolveContainedEntry(string id, [NotNullWhen(true)] out ISc
IEnumerable<IScopedNode> IScopedNode.Children(string? name) => this.GetElementPairs()
.Where(ep => (name == null || name == ep.Key))
.SelectMany<KeyValuePair<string, object>, Base>(ep =>
(ep.Key, ep.Value) switch
ep.Value switch
{
(_, Base b) => (IEnumerable<Base>)[b.WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
(_, IEnumerable<Base> list) => list.Select((item, idx) => item.WithScopeInfo(new ScopeInformation(this, ep.Key, idx))),
("url", string s) when this is Extension => [new FhirUri(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
("id", string s) when this is Element => [new FhirString(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
("value", _) => [],
_ => throw new InvalidOperationException("Unexpected system primitive in child list")
Base b => (IEnumerable<Base>)[b.WithScopeInfo(new ScopeInformation(this, ep.Key, null))],
IEnumerable<Base> list => list.Select((item, idx) => item.WithScopeInfo(new ScopeInformation(this, ep.Key, idx))),
_ => throw new InvalidOperationException($"Key '{ep.Key}' has a value with " +
$"unexpected type '{ep.Value.GetType()}'.")
}
);

Expand Down
16 changes: 7 additions & 9 deletions src/Hl7.Fhir.Base/Model/Base.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,12 @@ POSSIBILITY OF SUCH DAMAGE.
#nullable enable

using Hl7.Fhir.ElementModel;
using Hl7.Fhir.Introspection;
using Hl7.Fhir.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;

namespace Hl7.Fhir.Model;
Expand All @@ -51,7 +48,6 @@ public abstract partial class Base : IDeepCopyable, IDeepComparable,
/// </summary>
public virtual string TypeName => GetType().Name;


private Dictionary<string, object>? _overflow = null;

/// <summary>
Expand All @@ -71,11 +67,11 @@ public abstract partial class Base : IDeepCopyable, IDeepComparable,
public IEnumerable<object> Annotations(Type type)
{
if (type == typeof(ITypedElement) || type == typeof(IShortPathGenerator) || type == typeof(IScopedNode))
return new[] { this };
return [this];
else if (type == typeof(IFhirValueProvider))
return new[] { this };
return [this];
else if (type == typeof(IResourceTypeSupplier))
return new[] { this };
return [this];
else
return annotations.OfType(type);
}
Expand All @@ -100,7 +96,11 @@ internal protected virtual Base SetValue(string key, object? value)
if (value is null)
Overflow.Remove(key);
else
{
if (value is not Base && value is not IReadOnlyList<Base>)
throw new InvalidCastException($"Value for key '{key}' must be of type Base or a list of type Base.");
Overflow[key] = value;
}

return this;
}
Expand All @@ -117,6 +117,4 @@ internal protected virtual bool TryGetValue(string key, [NotNullWhen(true)] out
Overflow.TryGetValue(key, out value);

internal protected virtual IEnumerable<KeyValuePair<string, object>> GetElementPairs() => Overflow;

// TODO bring Children + NamedChildren over as well.
}
30 changes: 0 additions & 30 deletions src/Hl7.Fhir.Base/Model/PrimitiveType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,36 +57,6 @@ protected virtual void OnObjectValueChanged()
return ObjectValue is null ? null : PrimitiveTypeConverter.ConvertTo<string>(ObjectValue);
}

/// <inheritdoc/>
internal protected override bool TryGetValue(string key, [NotNullWhen(true)] out object? value)
{
if (key == "value")
{
value = ObjectValue;
return value is not null;
}
else
return base.TryGetValue(key, out value);
}

internal protected override Base SetValue(string key, object? value)
{
switch (key)
{
case "value":
ObjectValue = value;
return this;
default:
return base.SetValue(key, value);
}
}
/// <inheritdoc/>
internal protected override IEnumerable<KeyValuePair<string, object>> GetElementPairs()
{
if (ObjectValue is not null) yield return new KeyValuePair<string, object>("value", ObjectValue);
foreach (var kvp in base.GetElementPairs()) yield return kvp;
}

/// <summary>
/// Returns true if the primitive has any child elements (currently in FHIR this can
/// be only the element id and zero or more extensions).
Expand Down
93 changes: 48 additions & 45 deletions src/Hl7.Fhir.Base/Serialization/BaseFhirJsonPocoSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using Hl7.Fhir.Specification;
using Hl7.Fhir.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -61,7 +60,7 @@ public BaseFhirJsonPocoSerializer(FhirRelease release, FhirJsonPocoSerializerSet
/// Serializes the given dictionary with FHIR data into Json.
/// </summary>
public void Serialize(Base element, Utf8JsonWriter writer) =>
serializeInternal(element, writer, skipValue: false);
serializeInternal(element, writer);

/// <summary>
/// Serializes the given dictionary with FHIR data into a Json string.
Expand All @@ -70,21 +69,28 @@ public string SerializeToString(Base element)
{
var stream = new MemoryStream();
var writer = new Utf8JsonWriter(stream);
serializeInternal(element, writer, skipValue: false);
serializeInternal(element, writer);
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}

/// <summary>
/// Serializes the given dictionary with FHIR data into Json, optionally skipping the "value" element.
/// Serializes the given POCO with FHIR data into Json, optionally skipping the "value" element.
/// </summary>
/// <remarks>Not serializing the "value" element is useful when serializing FHIR primitives into two properties, one
/// with just the value, and one with the id/extensions.</remarks>
private void serializeInternal(
Base element,
Utf8JsonWriter writer,
bool skipValue)
Base? element,
Utf8JsonWriter writer
)
{
if (element is null)
{
// empty objects in arrays may occur in error situations.
writer.WriteNullValue();
return;
}

writer.WriteStartObject();
var filter = Settings.SummaryFilter;

Expand All @@ -100,8 +106,6 @@ private void serializeInternal(

foreach (var member in element.GetElementPairs())
{
if (skipValue && member.Key == "value") continue;

var propertyMapping = mapping?.FindMappedElementByName(member.Key);

if (filter?.TryEnterMember(member.Key, member.Value, propertyMapping) == false)
Expand All @@ -116,25 +120,33 @@ private void serializeInternal(
? (member.Value is Integer64 ? typeof(Integer64) : null)
: propertyMapping?.FhirType.FirstOrDefault();

if (member.Value is PrimitiveType pt)
serializeFhirPrimitive(propertyName, pt, writer, requiredType);
else if (member.Value is IReadOnlyCollection<PrimitiveType?> pts)
serializeFhirPrimitiveList(propertyName, pts, writer, requiredType);
else
switch (member.Value)
{
writer.WritePropertyName(propertyName);

if (member.Value is ICollection coll and not byte[])
{
writer.WriteStartArray();

foreach (var value in coll)
serializeMemberValue(value, writer, requiredType);

writer.WriteEndArray();
}
else
serializeMemberValue(member.Value, writer, requiredType);
case PrimitiveType pt:
serializeFhirPrimitive(propertyName, pt, writer, requiredType);
break;
case IReadOnlyList<PrimitiveType?> pts:
serializeFhirPrimitiveList(propertyName, pts, writer, requiredType);
break;
case IReadOnlyList<Base?> children: // Not List<Base>, since that is an invariant type.
{
writer.WritePropertyName(propertyName);
writer.WriteStartArray();

foreach (var child in children)
serializeInternal(child, writer);

writer.WriteEndArray();
break;
}
case Base b:
{
writer.WritePropertyName(propertyName);
serializeInternal(b, writer);
break;
}
default:
throw new InvalidOperationException($"GetElementPairs() returned a non-Base element of type {member.Value.GetType()}.");
}

filter?.LeaveMember(member.Key, member.Value, propertyMapping);
Expand All @@ -153,15 +165,7 @@ private static string addSuffixToElementName(string elementName, object elementV
_ => null
};

return typeName is null ? elementName : elementName + char.ToUpperInvariant(typeName[0]) + typeName.Substring(1);
}

private void serializeMemberValue(object value, Utf8JsonWriter writer, Type? requiredType = null)
{
if (value is Base complex)
serializeInternal(complex, writer, skipValue: false);
else
SerializePrimitiveValue(value, writer, requiredType);
return typeName is null ? elementName : elementName + char.ToUpperInvariant(typeName[0]) + typeName[1..];
}

/// <summary>
Expand All @@ -172,7 +176,7 @@ private void serializeMemberValue(object value, Utf8JsonWriter writer, Type? req
/// may use Json <c>null</c>s as placeholders.</remarks>
private void serializeFhirPrimitiveList(
string elementName,
IReadOnlyCollection<PrimitiveType?> values,
IReadOnlyList<PrimitiveType?> values,
Utf8JsonWriter writer,
Type? requiredType = null)
{
Expand All @@ -198,7 +202,7 @@ private void serializeFhirPrimitiveList(
writeStartArray(elementName, numNullsMissed, writer);
}

SerializePrimitiveValue(value!.ObjectValue, writer, requiredType);
SerializePrimitiveValue(value.ObjectValue, writer, requiredType);
}
else
{
Expand Down Expand Up @@ -228,7 +232,7 @@ private void serializeFhirPrimitiveList(
writeStartArray("_" + elementName, numNullsMissed, writer);
}

serializeInternal(value, writer, skipValue: true);
serializeInternal(value, writer);
}
else
{
Expand Down Expand Up @@ -267,12 +271,11 @@ private void serializeFhirPrimitive(string elementName, PrimitiveType value, Utf
SerializePrimitiveValue(value.ObjectValue, writer, requiredType);
}

if (value.HasElements)
{
// Write a property with '_elementName'
writer.WritePropertyName("_" + elementName);
serializeInternal(value, writer, skipValue: true);
}
if (!value.HasElements) return;

// Write a property with '_elementName'
writer.WritePropertyName("_" + elementName);
serializeInternal(value, writer);
}

/// <summary>
Expand Down
Loading

0 comments on commit 4290945

Please sign in to comment.