Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/Hl7.Fhir.Base/Serialization/BaseFhirJsonPocoDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ FhirJsonPocoDeserializerState state
{
object? result;
var oldErrorCount = state.Errors.Count;
var (line, pos) = reader.CurrentState.GetLocation();
var (line, pos) = reader.GetLocation();

// There might be an existing value, since FhirPrimitives may be spread out over two properties
// (one with, and one without the '_')
Expand Down Expand Up @@ -392,6 +392,9 @@ FhirJsonPocoDeserializerState state
}
}

if (Settings.AnnotateLineInfo && result is Base b)
b.AddAnnotation(new JsonSerializationDetails { LineNumber = (int)line, LinePosition = (int)pos });

// Only do validation when no parse errors were encountered, otherwise we'll just
// produce spurious messages.
if (Settings.Validator is not null && (Settings.ValidateOnFailedParse || oldErrorCount == state.Errors.Count))
Expand Down Expand Up @@ -456,7 +459,12 @@ FhirJsonPocoDeserializerState state
// to simply create a list by Adding(). Not the fastest approach :-(
while (reader.TokenType != JsonTokenType.EndArray)
{
var (line, pos) = reader.GetLocation();

var result = deserializeSingleValue(ref reader, propertyValueMapping, propertyMapping, state);
if (Settings.AnnotateLineInfo && result is Base b)
b.AddAnnotation(new JsonSerializationDetails { LineNumber = (int)line, LinePosition = (int)pos, ArrayIndex = listInstance.Count });

listInstance.Add(result);
state.Path.IncrementIndex();

Expand Down Expand Up @@ -548,6 +556,7 @@ FhirJsonPocoDeserializerState state

while (reader.TokenType != JsonTokenType.EndArray)
{
var (line, pos) = reader.GetLocation();
if (elementIndex >= originalSize)
existingList.Add(null);

Expand All @@ -564,6 +573,9 @@ FhirJsonPocoDeserializerState state
onlyNulls = false;
_ = DeserializeFhirPrimitive((PrimitiveType)existingList[elementIndex]!, propertyName, propertyValueMapping, fhirType, ref reader, delayedValidations, state);

if (Settings.AnnotateLineInfo && existingList[elementIndex] is Base b)
b.AddAnnotation(new JsonSerializationDetails { LineNumber = (int)line, LinePosition = (int)pos, ArrayIndex = elementIndex });

delayedValidations.SetPropertyIndex(propertyName, existingList.Count);
}

Expand Down
14 changes: 12 additions & 2 deletions src/Hl7.Fhir.Base/Serialization/BaseFhirXmlPocoDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ private void deserializeElementInto(Base target, ClassMapping mapping, XmlReader
var hasValueAttribute = reader.GetAttribute("value") != null;
var depth = reader.Depth;
var name = reader.LocalName;

if (Settings.AnnotateLineInfo)
target.AddAnnotation(new XmlSerializationDetails { LineNumber = lineNumber, LinePosition = position });

//check if on opening tag
if (reader.NodeType != XmlNodeType.Element)
Expand Down Expand Up @@ -333,7 +336,7 @@ private void deserializePropertyValue(Base target, XmlReader reader, FhirXmlPoco
{
state.Errors.Add(ERR.INVALID_DUPLICATE_PROPERTY(reader, state.Path.GetInstancePath(), propMapping.Name));
}

if (Settings.Validator is not null && (Settings.ValidateOnFailedParse || oldErrors == state.Errors.Count))
{
var context = new PropertyDeserializationContext(
Expand All @@ -344,6 +347,9 @@ private void deserializePropertyValue(Base target, XmlReader reader, FhirXmlPoco

PocoDeserializationHelper.RunPropertyValidation(ref result, Settings.Validator, context, state.Errors);
}

if (Settings.AnnotateLineInfo && result is Base b)
b.AddAnnotation(new XmlSerializationDetails { LineNumber = lineNumber, LinePosition = position });

propMapping.SetValue(target, result);
}
Expand Down Expand Up @@ -513,9 +519,10 @@ private void readAttribute(Base target, PropertyMapping propMapping, string elem

if (parsedValue != null)
{
var (lineNumber, position) = reader.GenerateLineInfo();

if (Settings.Validator is not null && (Settings.ValidateOnFailedParse || oldErrors == state.Errors.Count))
{
var (lineNumber, position) = reader.GenerateLineInfo();
var name = reader.LocalName;

var context = new PropertyDeserializationContext(
Expand All @@ -530,6 +537,9 @@ private void readAttribute(Base target, PropertyMapping propMapping, string elem
if (target is PrimitiveType primitive && propMapping.Name == "value")
{
primitive.ObjectValue = parsedValue;

if (Settings.AnnotateLineInfo)
target.AddAnnotation(new XmlSerializationDetails { LineNumber = lineNumber, LinePosition = position });
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ public record FhirJsonPocoDeserializerSettings
/// resource was clean and possibly ok to process).
/// </remarks>
public bool AnnotateResourceParseExceptions { get; init; } = false;

/// <summary>
/// Enable annotating line information of the parsed resources and properties.
/// </summary>
/// <remarks>
/// This has a big impact on memory usage, as every element has to be aware where it was in the source data.
/// It is recommended to be kept disabled.
/// </remarks>
public bool AnnotateLineInfo { get; init; } = false;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ public class FhirXmlPocoDeserializerSettings
/// resource was clean and possibly ok to process).
/// </remarks>
public bool AnnotateResourceParseExceptions { get; init; } = false;

/// <summary>
/// Enable annotating line information of the parsed resources and properties.
/// </summary>
/// <remarks>
/// This has a big impact on memory usage, as every element has to be aware where it was in the source data.
/// It is recommended to be kept disabled.
/// </remarks>
public bool AnnotateLineInfo { get; init; } = false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ParseDemoPatient.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParseDemoPatientJsonTyped.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParseDemoPatientJsonUntyped.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParseDemoPatientLineInfoAnnotationPoco.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParseDemoPatientXmlTyped.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParseDemoPatientXmlUntyped.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ParseXMLElements.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using FluentAssertions;
using Hl7.Fhir.Model;
using Hl7.Fhir.Utility;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections;
using System.IO;

namespace Hl7.Fhir.Serialization.Tests
{
[TestClass]
public class ParseDemoPatientLineInfoAnnotationPoco
Comment thread
andrzejskowronski marked this conversation as resolved.
{
private static T getXmlPocoAnnotated<T>(string xml) where T : Resource
{
new FhirXmlPocoDeserializer(new FhirXmlPocoDeserializerSettings(){ AnnotateLineInfo = true }).TryDeserializeResource(xml, out var resource, out _);
return (T)resource;
}

private static T getJsonPocoAnnotated<T>(string json) where T : Resource
{
new FhirJsonPocoDeserializer(new FhirJsonPocoDeserializerSettings(){ AnnotateLineInfo = true }).TryDeserializeResource(json, out var resource, out _);
return (T)resource;
}

[TestMethod]
public void HasLineNumbers_PocoFromXml()
{
var xml = File.ReadAllText(Path.Combine("TestData", "fp-test-patient.xml"));
var nav = getXmlPocoAnnotated<Patient>(xml);

foreach (var c in nav.Children)
{
CheckAllElementsAnnotated<XmlSerializationDetails>(c);
}
}

[TestMethod]
public void HasLineNumbers_PocoFromJson()
{
var json = File.ReadAllText(Path.Combine("TestData", "fp-test-patient.json"));
var nav = getJsonPocoAnnotated<Patient>(json);

foreach (var c in nav.Children)
{
CheckAllElementsAnnotated<JsonSerializationDetails>(c);
}
}

public void CheckAllElementsAnnotated<T>(object element) where T : IPositionInfo
{
Assert.IsNotNull(element);
if (element is Base baseElement)
{
var posInfo = baseElement.Annotation<T>();

posInfo.Should().NotBeNull();
posInfo.LineNumber.Should().NotBe(-1).And.NotBe(0);
posInfo.LinePosition.Should().NotBe(-1).And.NotBe(0);

foreach (var (_, baseChild) in baseElement)
{
CheckAllElementsAnnotated<T>(baseChild);
}
}

if (element is IList list)
{
foreach (var listElement in list)
{
CheckAllElementsAnnotated<T>(listElement);
}
}
}
}
}