public async Task GivenConfigurationSetAndFhirResources_WhenTransform_CdmFolderShouldBeGenerated() { string cdmFolder = Guid.NewGuid().ToString("N"); BaseMappingDefinitionLoader loader = new LocalMappingDefinitionLoader("TestResource\\testSchema"); IEnumerable <TabularMappingDefinition> tabularMappings = loader.Load(); CdmCorpusDefinition defination = CdmSchemaGenerator.InitLocalcdmCorpusDefinition(cdmFolder); CdmSchemaGenerator cdmSchemaGenerator = new CdmSchemaGenerator(defination); await cdmSchemaGenerator.InitializeCdmFolderAsync(tabularMappings); ISource source = new LocalNdjsonSource(Path.Combine("TestResource", "FhirResource")); ISink sink = new LocalCsvSink(cdmFolder) { CsvFilePath = (string tableName) => { return($"data/Local{tableName}/{tableName}-partition-data.csv"); } }; var transformer = new BasicFhirElementTabularTransformer(); FhirElementTabularTransformer.IdGenerator = () => { return("0000"); }; TransformationExecutor executor = new TransformationExecutor(source, sink, tabularMappings, transformer); await executor.ExecuteAsync(); // Whether generated CDM schema Assert.IsTrue(File.Exists(Path.Combine(cdmFolder, "default.manifest.cdm.json"))); Assert.IsTrue(File.Exists(Path.Combine(cdmFolder, "LocalPatient.cdm.json"))); Assert.IsTrue(File.Exists(Path.Combine(cdmFolder, "LocalPatientName.cdm.json"))); // If generated flatten data Assert.IsTrue(Directory.Exists(Path.Combine(cdmFolder, "data", "LocalPatient"))); Assert.IsTrue(Directory.Exists(Path.Combine(cdmFolder, "data", "LocalPatientName"))); // If generated data are same with ground truth string[] tableNames = { "Patient", "AllergyIntolerance", "CarePlan", "Encounter", "Location", "Observation", "PatientName", "PatientFlattenJson", "EncounterClass", "CarePlanPeriod" }; foreach (var tableName in tableNames) { var sourceFilePath = Path.Combine("TestResource", "TestOutput", $"Local{tableName}", $"{tableName}-partition-data.csv"); var targetFilePath = Path.Combine(@$ "{cdmFolder}", "data", $"Local{tableName}", $"{tableName}-partition-data.csv"); Assert.IsTrue(CheckSameContent(sourceFilePath, targetFilePath), $"{sourceFilePath} and {targetFilePath} are different."); } }
public void GivenAFhirResourceAndColumnDefinition_WhenToTabularSource_ValuesAndTypesShouldBeReturn() { BasicFhirElementTabularTransformer t = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Address.Add(new Address() { City = "Shanghai" }); ElementNode node = ElementNode.FromElement(patient.ToTypedElement()); TabularMappingDefinition defination = new TabularMappingDefinition("Test"); defination.Root.Children["address"] = new DefinitionNode(new ColumnDefinition("column1", "string")); defination.Root.Children["address"].Children["city"] = new DefinitionNode(new ColumnDefinition("column2", "string")); Dictionary <string, (object, object)> result = t.ToTabular(node, defination); Assert.AreEqual("{\"city\":\"Shanghai\"}", result["column1"].Item1); Assert.AreEqual("string", result["column1"].Item2); Assert.AreEqual("Shanghai", result["column2"].Item1); Assert.AreEqual("string", result["column2"].Item2); }
public void GivenStringRelated_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new FhirString("").ToTypedElement()), (new string(""), "string")), (node : ElementNode.FromElement(new FhirString("A sequence of Unicode characters").ToTypedElement()), (new string("A sequence of Unicode characters"), "string")), (node : ElementNode.FromElement(new Canonical("A URI that refers to a resource by its canonical URL").ToTypedElement()), (new string("A URI that refers to a resource by its canonical URL"), "canonical")), (node : ElementNode.FromElement(new Code("A date, or partial date as used in human communication").ToTypedElement()), (new string("A date, or partial date as used in human communication"), "code")), (node : ElementNode.FromElement(new Id("Any combination of upper- or lower-case ASCII letters").ToTypedElement()), (new string("Any combination of upper- or lower-case ASCII letters"), "id")), (node : ElementNode.FromElement(new Markdown("A FHIR string that may contain markdown").ToTypedElement()), (new string("A FHIR string that may contain markdown"), "markdown")), (node : ElementNode.FromElement(new Oid("urn:oid:1.2.3.4.5").ToTypedElement()), (new string("urn:oid:1.2.3.4.5"), "oid")), (node : ElementNode.FromElement(new Uuid("urn:uuid:c757873d-ec9a-4326-a141-556f43239520").ToTypedElement()), (new string("urn:uuid:c757873d-ec9a-4326-a141-556f43239520"), "uuid")), (node : ElementNode.FromElement(new FhirUri("https://www.hl7.org/fhir").ToTypedElement()), (new string("https://www.hl7.org/fhir"), "uri")), (node : ElementNode.FromElement(new FhirUrl("https://www.hl7.org/fhir").ToTypedElement()), (new string("https://www.hl7.org/fhir"), "url")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, example.Item2.expectedType.ToString()); Assert.AreEqual(transformResult, example.Item2); } }
public void GivenColumnDefinitionWithFhirExpressionInChildren_WhenToTabularSource_EvaluatedValueShourlBeReturn() { BasicFhirElementTabularTransformer t = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Address.Add(new Address() { City = "Shanghai" }); patient.Address.Add(new Address() { City = "Beijing" }); ElementNode node = ElementNode.FromElement(patient.ToTypedElement()); TabularMappingDefinition defination = new TabularMappingDefinition("Test"); defination.Root.Children[""] = new DefinitionNode(new ColumnDefinition("column1", "string", "address.last()", null), new ColumnDefinition("column2", "string", "address.first().city", null)); Dictionary <string, (object, object)> result = t.ToTabular(node, defination); Assert.AreEqual("{\"city\":\"Beijing\"}", result["column1"].Item1); Assert.AreEqual("Shanghai", result["column2"].Item1); }
public void GivenAFhirDateTimeNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new FhirDateTime("2015").ToTypedElement()), (new DateTime(2015, 1, 1), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-05").ToTypedElement()), (new DateTime(2015, 5, 1), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-05-04").ToTypedElement()), (new DateTime(2015, 5, 4), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2016-05-04T12:00:00").ToTypedElement()), (new DateTime(2016, 5, 4, 12, 0, 0), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2016-05-04T12:00:00+00:00").ToTypedElement()), (new DateTime(2016, 5, 4, 12, 0, 0), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2016-05-04T12:00:00Z").ToTypedElement()), (new DateTime(2016, 5, 4, 12, 0, 0), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-02-07T13:28:17").ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-02-07T13:28:17.159").ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17, 159), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-02-07T19:28:17-07:00").ToTypedElement()), (new DateTime(2015, 2, 8, 2, 28, 17), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-02-07T13:28:17.159-05:00").ToTypedElement()), (new DateTime(2015, 2, 7, 18, 28, 17, 159), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-02-07T13:28:17Z").ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17), "dateTime")), (node : ElementNode.FromElement(new FhirDateTime("2015-02-07T13:28:17.159Z").ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17, 159), "dateTime")), (node : null, (null, "dateTime")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.DateTime); Assert.AreEqual(transformResult, example.Item2); } }
static async Task Main(string[] args) { TransformationLogging.LoggerFactory = LoggerFactory.Create(builder => { builder.AddFilter("Microsoft", LogLevel.Warning) .AddFilter("System", LogLevel.Warning) .AddFilter("Microsoft.Health.Fhir.Transformation", LogLevel.Information) .AddConsole(); }); ILogger logger = TransformationLogging.CreateLogger <Program>(); var rootCommand = new RootCommand() { new Option <string>("--config"), new Option <string>("--input"), new Option <string>("--output"), new Option <int>("--maxDepth", getDefaultValue: () => 3), }; rootCommand.Handler = CommandHandler.Create( new Func <string, string, string, int, Task>(async(config, input, output, maxDepth) => { LocalMappingDefinitionLoader configLoader = new LocalMappingDefinitionLoader(config, maxDepth); TabularMappingDefinition[] mappings = configLoader.Load(); FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); logger.LogInformation("Start to generate CDM schema."); CdmCorpusDefinition defination = CdmSchemaGenerator.InitLocalcdmCorpusDefinition(output); CdmSchemaGenerator cdmSchemaGenerator = new CdmSchemaGenerator(defination); cdmSchemaGenerator.InitializeCdmFolderAsync(mappings).Wait(); logger.LogInformation("Generate CDM schema completed."); string operationId = Guid.NewGuid().ToString("N"); ISource source = new LocalNdjsonSource(input); ISink sink = new LocalCsvSink(output) { CsvFilePath = (string tableName) => { return($"data/Local{tableName}/partition-data-{operationId}.csv"); } }; logger.LogInformation("Start to transform data."); IProgress <(int, int)> progressHandler = new Progress <(int, int)>(progress => { if (progress.Item1 % 100 == 0 || progress.Item2 % 100 == 0) { logger.LogInformation($"({progress.Item1} loaded, {progress.Item2} transformed) to CDM folder. {DateTime.UtcNow.ToLongTimeString()}"); } }); TransformationExecutor executor = new TransformationExecutor(source, sink, mappings, transformer); await executor.ExecuteAsync(progressHandler); logger.LogInformation("Complete to transform data."); })); await rootCommand.InvokeAsync(args); }
public void GivenANodeWithInvalidValue_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); ElementNode node = ElementNode.FromElement(new Date("").ToTypedElement()); Assert.AreEqual(transformer.ConvertElementNode(node, FhirTypeNames.Date), (null, "date")); node.Value = null; Assert.AreEqual(transformer.ConvertElementNode(node, FhirTypeNames.Date), (null, "date")); node.Value = "123"; Assert.AreEqual(transformer.ConvertElementNode(node, FhirTypeNames.Date), (null, "date")); }
public void GivenAFhirDateNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new Date("2015").ToTypedElement()), (new DateTime(2015, 1, 1), "date")), (node : ElementNode.FromElement(new Date("2015-05").ToTypedElement()), (new DateTime(2015, 5, 1), "date")), (node : ElementNode.FromElement(new Date("2015-05-04").ToTypedElement()), (new DateTime(2015, 5, 4), "date")), (node : null, (null, "date")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.Date); Assert.AreEqual(transformResult, example.Item2); } }
public void GivenAFhirDecimalNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new FhirDecimal((decimal)1).ToTypedElement()), ((float)(1), "decimal")), (node : ElementNode.FromElement(new FhirDecimal((decimal)1.1).ToTypedElement()), ((float)(1.1), "decimal")), (node : ElementNode.FromElement(new FhirDecimal((decimal)1.1e5).ToTypedElement()), ((float)110000, "decimal")), (node : null, (null, "decimal")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.Decimal); Assert.AreEqual(transformResult, example.Item2); } }
public void GivenAFhirUnsignedIntNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new UnsignedInt(int.MaxValue).ToTypedElement()), (int.MaxValue, "unsignedInt")), (node : ElementNode.FromElement(new UnsignedInt(0).ToTypedElement()), ((int)(0), "unsignedInt")), (node : ElementNode.FromElement(new UnsignedInt(-1).ToTypedElement()), ((int)(-1), "unsignedInt")), (node : null, (null, "unsignedInt")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.UnsignedInt); Assert.AreEqual(transformResult, example.Item2); } }
public void GivenAFhirTimeNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new Time("01:00:00").ToTypedElement()), (new DateTime(0001, 1, 1, 1, 0, 0), "time")), (node : ElementNode.FromElement(new Time("12:00:00").ToTypedElement()), (new DateTime(0001, 1, 1, 12, 0, 0), "time")), (node : ElementNode.FromElement(new Time("00:00:00").ToTypedElement()), (new DateTime(0001, 1, 1, 0, 0, 0), "time")), (node : ElementNode.FromElement(new Time("05:01:00.159").ToTypedElement()), (new DateTime(0001, 1, 1, 5, 1, 0, 159), "time")), (node : null, (null, "time")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.Time); Assert.AreEqual(transformResult, example.Item2); } }
public void GivenAFhirBase64BinaryNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new Base64Binary(Encoding.ASCII.GetBytes("A stream of bytes, base64 encoded")).ToTypedElement()), (new string("A stream of bytes, base64 encoded"), "base64Binary")), (node : ElementNode.FromElement(new Base64Binary(Encoding.ASCII.GetBytes("")).ToTypedElement()), (new string(""), "base64Binary")), (node : null, (null, "base64Binary")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { string expectedString = example.Item2.expectedValue == null? null :Convert.ToBase64String(Encoding.ASCII.GetBytes(example.Item2.expectedValue.ToString())); var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.Base64Binary); Assert.AreEqual(transformResult, (expectedString, example.Item2.expectedType)); } }
public void GivenColumnDefinitionWithFhirExpressionInRoot_WhenToTabularSource_EvaluatedValueShourlBeReturn() { BasicFhirElementTabularTransformer t = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Id = "TestId"; patient.BirthDate = "1974-12-25"; ElementNode node = ElementNode.FromElement(patient.ToTypedElement()); TabularMappingDefinition defination = new TabularMappingDefinition("Test"); defination.Root.Children[""] = new DefinitionNode(new ColumnDefinition("column1", "string", "id", null), new ColumnDefinition("column2", "string", "birthDate", null)); Dictionary <string, (object, object)> result = t.ToTabular(node, defination); Assert.AreEqual("TestId", result["column1"].Item1); Assert.AreEqual("1974-12-25", result["column2"].Item1); }
public void GivenAFhirInstantNode_AfterTransforming_CorrectDateTimeInstanceShouldBeReturned() { List <(ElementNode node, (object expectedValue, object expectedType))> examples = new List <(ElementNode node, (object expectedValue, object expectedType))>() { (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-05-04Z")).ToTypedElement()), (new DateTime(2015, 5, 4), "instant")), (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-02-07T13:28:17Z")).ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17), "instant")), (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-02-07T13:28:17.159Z")).ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17, 159), "instant")), (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-02-07T13:28:17-05:00")).ToTypedElement()), (new DateTime(2015, 2, 7, 18, 28, 17), "instant")), (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-02-07T13:28:17.159-05:00")).ToTypedElement()), (new DateTime(2015, 2, 7, 18, 28, 17, 159), "instant")), (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-02-07T13:28:17Z")).ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17), "instant")), (node : ElementNode.FromElement(new Instant(DateTimeOffset.Parse("2015-02-07T13:28:17.159Z")).ToTypedElement()), (new DateTime(2015, 2, 7, 13, 28, 17, 159), "instant")), (node : null, (null, "instant")) }; FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); foreach (var example in examples) { var transformResult = transformer.ConvertElementNode(example.node, FhirTypeNames.Instant); Assert.AreEqual(transformResult, example.Item2); } }
public void GivenUnrollPathConfiguration_WhenToTabularSource_MultipleItemsShouldBeReturn() { BasicFhirElementTabularTransformer t = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Address.Add(new Address() { City = "Shanghai" }); patient.Address.Add(new Address() { City = "Beijing" }); patient.BirthDate = "1974-12-25"; patient.Id = "Test1"; patient.Telecom.Add(new ContactPoint() { Rank = 1 }); TabularMappingDefinition defination = new TabularMappingDefinition("Test"); defination.Unrollpath = "Patient.address"; defination.Root.Children["city"] = new DefinitionNode(new ColumnDefinition("column1", "string")); IEnumerable <Dictionary <string, (object, object)> > results = t.ToTabular(patient, defination); Assert.AreEqual(2, results.Count()); foreach (var result in results) { Assert.AreEqual("Test1", result[ReservedColumnName.ResourceId].Item1); Assert.IsTrue(result.ContainsKey(ReservedColumnName.FhirPath)); Assert.IsTrue(result.ContainsKey("column1")); Assert.IsTrue(!string.IsNullOrEmpty(result["column1"].Item1?.ToString())); } }
public void GivenConfigurationWithInvalidPath_WhenToTabularSource_NullValueShouldBeReturn() { BasicFhirElementTabularTransformer t = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Address.Add(new Address() { City = "Shanghai" }); ElementNode node = ElementNode.FromElement(patient.ToTypedElement()); TabularMappingDefinition defination = new TabularMappingDefinition("Test"); defination.Root.Children["invalid"] = new DefinitionNode(); defination.Root.Children["invalid"].Children["invalid"] = new DefinitionNode(new ColumnDefinition("column1", "string")); Dictionary <string, (object, object)> result = t.ToTabular(node, defination); Assert.IsNull(result["column1"].Item1); Assert.AreEqual("string", result["column1"].Item2); }
public void GivenColumnDefinitionWithDifferentTypes_WhenToTabularSource_ValueCanBeConvertedShouldBeReturn() { BasicFhirElementTabularTransformer t = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Address.Add(new Address() { City = "Shanghai" }); patient.BirthDate = "1974-12-25"; patient.Telecom.Add(new ContactPoint() { Rank = 1 }); ElementNode node = ElementNode.FromElement(patient.ToTypedElement()); TabularMappingDefinition defination = new TabularMappingDefinition("Test"); defination.Root.Children["address"] = new DefinitionNode(); defination.Root.Children["address"].Children["city"] = new DefinitionNode(new ColumnDefinition("column1", "string")); defination.Root.Children["birthDate"] = new DefinitionNode(new ColumnDefinition("column2", "date")); defination.Root.Children["telecom"] = new DefinitionNode(); defination.Root.Children["telecom"].Children["rank"] = new DefinitionNode(new ColumnDefinition("column3", "integer")); Dictionary <string, (object, object)> result = t.ToTabular(node, defination); Assert.AreEqual("Shanghai", result["column1"].Item1); Assert.AreEqual("string", result["column1"].Item2); Assert.AreEqual(DateTime.Parse("1974-12-25"), result["column2"].Item1); Assert.AreEqual("date", result["column2"].Item2); Assert.AreEqual(1, result["column3"].Item1); Assert.AreEqual("integer", result["column3"].Item2); }
public void GivenAFhirArrayNodeThatHasParent_AfterTransformingUsingAsJson_CorrectStringInstanceShouldBeReturned() { FhirElementTabularTransformer transformer = new BasicFhirElementTabularTransformer(); Patient patient = new Patient() { Gender = AdministrativeGender.Male }; patient.Name.Add(new HumanName() { Use = HumanName.NameUse.Official, Family = "Chalmers", Given = new List <string>() { "Peter", "James" } }); patient.Name.Add(new HumanName() { Use = HumanName.NameUse.Usual, Given = new List <string>() { "Jim" } }); patient.Name.Add(new HumanName() { Use = HumanName.NameUse.Maiden, Family = "Windsor", Given = new List <string>() { "Peter" } }); ElementNode node = ElementNode.FromElement(patient.ToTypedElement()); var expected = ("[{\"use\":\"official\",\"family\":\"Chalmers\",\"given\":[\"Peter\",\"James\"]},{\"use\":\"usual\",\"given\":[\"Jim\"]},{\"use\":\"maiden\",\"family\":\"Windsor\",\"given\":[\"Peter\"]}]" , "array"); Assert.AreEqual(transformer.ConvertElementNode(node["name"].FirstOrDefault(), FhirTypeNames.Array), expected); }