diff --git a/cc/google/fhir/BUILD b/cc/google/fhir/BUILD index 6933d55c7..417ed9250 100644 --- a/cc/google/fhir/BUILD +++ b/cc/google/fhir/BUILD @@ -85,12 +85,15 @@ cc_library( deps = [ ":annotations", ":core_resource_registry", - ":type_macros", + ":proto_util", ":util", + "//cc/google/fhir/status", "//cc/google/fhir/status:statusor", + "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", "@com_google_protobuf//:protobuf", "@com_googlesource_code_re2//:re2", ], @@ -327,6 +330,7 @@ cc_library( ":extensions", ":fhir_types", ":json_format_results", + ":json_util", ":primitive_handler", ":primitive_wrapper", ":proto_util", @@ -340,6 +344,7 @@ cc_library( "//cc/google/fhir/status", "//cc/google/fhir/status:statusor", "//proto/google/fhir/proto:annotations_cc_proto", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", @@ -480,10 +485,12 @@ cc_library( hdrs = ["json_util.h"], strip_include_prefix = "//cc/", deps = [ + ":annotations", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_protobuf//:protobuf", ], ) @@ -521,10 +528,9 @@ cc_test( deps = [ ":json_util", "//cc/google/fhir/status", - "//cc/google/fhir/status:statusor", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", + "//proto/google/fhir/proto/r4/core:datatypes_cc_proto", + "//proto/google/fhir/proto/r4/core/resources:observation_cc_proto", + "//proto/google/fhir/proto/r4/core/resources:patient_cc_proto", "@com_google_googletest//:gtest_main", ], ) diff --git a/cc/google/fhir/fhir_path/BUILD b/cc/google/fhir/fhir_path/BUILD index 02061a1b7..bd54454b8 100644 --- a/cc/google/fhir/fhir_path/BUILD +++ b/cc/google/fhir/fhir_path/BUILD @@ -82,6 +82,7 @@ cc_library( ":fhir_path", "//cc/google/fhir:annotations", "//cc/google/fhir:error_reporter", + "//cc/google/fhir:json_util", "//cc/google/fhir:primitive_handler", "//cc/google/fhir:proto_util", "//cc/google/fhir:util", @@ -89,12 +90,9 @@ cc_library( "//cc/google/fhir/status:statusor", "//proto/google/fhir/proto:annotations_cc_proto", "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/types:optional", "@com_google_protobuf//:protobuf", ], ) @@ -181,9 +179,10 @@ cc_library( strip_include_prefix = "//cc/", visibility = [":__pkg__"], deps = [ + "//cc/google/fhir:annotations", + "//cc/google/fhir:json_util", "//cc/google/fhir:references", "//cc/google/fhir:util", - "//cc/google/fhir/status", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", diff --git a/cc/google/fhir/fhir_path/fhir_path_validation.cc b/cc/google/fhir/fhir_path/fhir_path_validation.cc index 2f989e8b2..9d27ab9e5 100644 --- a/cc/google/fhir/fhir_path/fhir_path_validation.cc +++ b/cc/google/fhir/fhir_path/fhir_path_validation.cc @@ -29,6 +29,7 @@ #include "google/fhir/annotations.h" #include "google/fhir/error_reporter.h" #include "google/fhir/fhir_path/fhir_path.h" +#include "google/fhir/json_util.h" #include "google/fhir/primitive_handler.h" #include "google/fhir/proto_util.h" #include "google/fhir/status/status.h" @@ -320,10 +321,11 @@ absl::Status ValidateConstraint(const internal::WorkspaceMessage& message, } std::string PathTerm(const Message& message, const FieldDescriptor* field) { - return IsContainedResource(message) || - IsChoiceTypeContainer(message.GetDescriptor()) - ? absl::StrCat("ofType(", field->message_type()->name(), ")") - : field->json_name(); + if (IsContainedResource(message) || + IsChoiceTypeContainer(message.GetDescriptor())) { + return absl::StrCat("ofType(", field->message_type()->name(), ")"); + } + return FhirJsonName(field); } namespace { diff --git a/cc/google/fhir/fhir_path/utils.cc b/cc/google/fhir/fhir_path/utils.cc index 07cfce522..802d856bd 100644 --- a/cc/google/fhir/fhir_path/utils.cc +++ b/cc/google/fhir/fhir_path/utils.cc @@ -15,9 +15,12 @@ #include "google/fhir/fhir_path/utils.h" #include "google/protobuf/any.pb.h" +#include "google/protobuf/descriptor.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "google/fhir/annotations.h" +#include "google/fhir/json_util.h" #include "google/fhir/references.h" #include "google/fhir/util.h" @@ -27,8 +30,8 @@ namespace fhir_path { namespace internal { using ::absl::NotFoundError; -using ::google::protobuf::FieldDescriptor; using ::google::protobuf::Descriptor; +using ::google::protobuf::FieldDescriptor; using ::google::protobuf::Message; using ::google::protobuf::Reflection; @@ -68,15 +71,14 @@ absl::Status OneofMessageFromContainer(const Message& container_message, namespace { -// Returns true if `message` is a Reference and field_descriptor is a field -// within `message` that resolves to 'reference' in FHIR. -// Note: the json_name corresponds to fields in FHIR. -bool IsReference(const Message& message, - const google::protobuf::FieldDescriptor& field_descriptor) { +// Returns true if `message` is a Reference and field_descriptor is the `uri` +// field. +bool IsReferenceUri(const Message& message, + const google::protobuf::FieldDescriptor& field_descriptor) { return message.GetDescriptor()->options().GetExtension( ::google::fhir::proto::fhir_structure_definition_url) == "http://hl7.org/fhir/StructureDefinition/Reference" && - field_descriptor.json_name() == "reference"; + field_descriptor.name() == "uri"; } } // namespace @@ -95,7 +97,7 @@ absl::Status RetrieveField( // If asked to retrieve a reference, retrieve a String value with a standard // representation (i.e. ResourceType/id) regardless of how the reference is // stored in the source message. - if (IsReference(root, field)) { + if (IsReferenceUri(root, field)) { Message* string_message = message_factory(field.message_type()); if (string_message == nullptr) { return absl::InternalError( @@ -162,7 +164,7 @@ bool HasFieldWithJsonName(const Descriptor* descriptor, const FieldDescriptor* FindFieldByJsonName(const Descriptor* descriptor, absl::string_view json_name) { for (int i = 0; i < descriptor->field_count(); ++i) { - if (json_name == descriptor->field(i)->json_name()) { + if (json_name == FhirJsonName(descriptor->field(i))) { return descriptor->field(i); } } diff --git a/cc/google/fhir/json_parser.cc b/cc/google/fhir/json_parser.cc index e4ec370e7..dd818817b 100644 --- a/cc/google/fhir/json_parser.cc +++ b/cc/google/fhir/json_parser.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -24,12 +25,15 @@ #include #include "google/protobuf/any.pb.h" +#include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/container/flat_hash_map.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "google/fhir/annotations.h" #include "google/fhir/core_resource_registry.h" @@ -39,6 +43,7 @@ #include "google/fhir/json/json_sax_handler.h" #include "google/fhir/json_format.h" #include "google/fhir/json_format_results.h" +#include "google/fhir/json_util.h" #include "google/fhir/primitive_wrapper.h" #include "google/fhir/r4/profiles.h" #include "google/fhir/references.h" @@ -75,6 +80,7 @@ MakeFieldMap(const Descriptor* descriptor) { std::unordered_map>(); for (int i = 0; i < descriptor->field_count(); i++) { const FieldDescriptor* field = descriptor->field(i); + const std::string& json_name = FhirJsonName(field); if (IsChoiceType(field)) { std::unique_ptr< const std::unordered_map> @@ -85,23 +91,23 @@ MakeFieldMap(const Descriptor* descriptor) { // Convert primitive extension field name to field on choice type, // e.g., value + _boolean -> _valueBoolean for Extension.value. child_field_name[1] = std::toupper(child_field_name[1]); - (*field_map)[absl::StrCat("_", field->json_name(), + (*field_map)[absl::StrCat("_", json_name, child_field_name.substr(1))] = field; } else { // For non-primitive, just append them together as camelcase, e.g., // value + boolean = valueBoolean child_field_name[0] = std::toupper(child_field_name[0]); - (*field_map)[absl::StrCat(field->json_name(), child_field_name)] = - field; + (*field_map)[absl::StrCat(json_name, child_field_name)] = field; } } } else { - (*field_map)[field->json_name()] = field; + (*field_map)[json_name] = field; + if (field->type() == FieldDescriptor::TYPE_MESSAGE && IsPrimitive(field->message_type())) { // Fhir JSON represents extensions to primitive fields as separate // standalone JSON objects, keyed by the "_" + field name. - (*field_map)["_" + field->json_name()] = field; + (*field_map)["_" + json_name] = field; } } } diff --git a/cc/google/fhir/json_printer.cc b/cc/google/fhir/json_printer.cc index aabf386e8..4523af5e0 100644 --- a/cc/google/fhir/json_printer.cc +++ b/cc/google/fhir/json_printer.cc @@ -36,6 +36,7 @@ #include "google/fhir/extensions.h" #include "google/fhir/fhir_types.h" #include "google/fhir/json_format.h" +#include "google/fhir/json_util.h" #include "google/fhir/primitive_handler.h" #include "google/fhir/proto_util.h" #include "google/fhir/r4/codeable_concepts.h" @@ -269,7 +270,7 @@ class Printer { // to query all possible choice types in a single query. if (IsChoiceType(field) && json_format_ == kFormatPure) { FHIR_RETURN_IF_ERROR(PrintChoiceTypeField( - reflection->GetMessage(proto, field), field->json_name())); + reflection->GetMessage(proto, field), FhirJsonName(field))); } else { FHIR_RETURN_IF_ERROR(PrintField(proto, field, print_as_string)); } @@ -347,7 +348,7 @@ class Printer { } else { int field_size = reflection->FieldSize(containing_proto, field); - PrintFieldPreamble(field->json_name()); + PrintFieldPreamble(FhirJsonName(field)); output_ += "["; Indent(); AddNewline(); @@ -370,9 +371,9 @@ class Printer { if (IsPrimitive(field->message_type())) { FHIR_RETURN_IF_ERROR( PrintPrimitiveField(reflection->GetMessage(containing_proto, field), - field->json_name())); + FhirJsonName(field))); } else { - PrintFieldPreamble(field->json_name()); + PrintFieldPreamble(FhirJsonName(field)); FHIR_RETURN_IF_ERROR( PrintNonPrimitive(reflection->GetMessage(containing_proto, field))); } @@ -432,7 +433,7 @@ class Printer { } const google::protobuf::FieldDescriptor* value_field = choice_reflection->GetOneofFieldDescriptor(choice_container, oneof); - std::string oneof_field_name = value_field->json_name(); + std::string oneof_field_name = FhirJsonName(value_field); oneof_field_name[0] = toupper(oneof_field_name[0]); if (IsPrimitive(value_field->message_type())) { @@ -474,7 +475,7 @@ class Printer { } if (non_null_values_found) { - PrintFieldPreamble(field->json_name()); + PrintFieldPreamble(FhirJsonName(field)); output_ += "["; Indent(); for (const JsonPrimitive& json_primitive : json_primitives) { @@ -493,7 +494,7 @@ class Printer { output_ += ","; AddNewline(); } - PrintFieldPreamble(absl::StrCat("_", field->json_name())); + PrintFieldPreamble(absl::StrCat("_", FhirJsonName(field))); output_ += "["; Indent(); for (const JsonPrimitive& json_primitive : json_primitives) { diff --git a/cc/google/fhir/json_util.cc b/cc/google/fhir/json_util.cc index 8bda7001d..069b745e3 100644 --- a/cc/google/fhir/json_util.cc +++ b/cc/google/fhir/json_util.cc @@ -18,10 +18,12 @@ #include +#include "google/protobuf/descriptor.h" #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "google/fhir/annotations.h" namespace google::fhir { @@ -60,4 +62,15 @@ absl::StatusOr ToJsonStringValue(absl::string_view raw_value) { return result; } +const std::string& FhirJsonName(const google::protobuf::FieldDescriptor* field) { + // When translating References between proto and JSON, the unstructured + // FHIR JSON Reference.reference field maps to the absolute URI field + // in the proto Reference. + static const std::string* kReferenceFieldName = new std::string("reference"); + if (field->name() == "uri" && IsReference(field->containing_type())) { + return *kReferenceFieldName; + } + return field->json_name(); +} + } // namespace google::fhir diff --git a/cc/google/fhir/json_util.h b/cc/google/fhir/json_util.h index 4ee380e73..c84ed44d1 100644 --- a/cc/google/fhir/json_util.h +++ b/cc/google/fhir/json_util.h @@ -19,6 +19,7 @@ #include +#include "google/protobuf/descriptor.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -44,6 +45,9 @@ namespace google::fhir { // http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf absl::StatusOr ToJsonStringValue(absl::string_view raw_value); +// Given a proto field, returns the name of the field in FHIR JSON. +const std::string& FhirJsonName(const google::protobuf::FieldDescriptor* field); + } // namespace google::fhir #endif // GOOGLE_FHIR_JSON_UTIL_H_ diff --git a/cc/google/fhir/json_util_test.cc b/cc/google/fhir/json_util_test.cc index 9e7c86873..ff31b63d7 100644 --- a/cc/google/fhir/json_util_test.cc +++ b/cc/google/fhir/json_util_test.cc @@ -16,13 +16,19 @@ #include "google/fhir/json_util.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "google/fhir/status/status.h" +#include "proto/google/fhir/proto/r4/core/datatypes.pb.h" +#include "proto/google/fhir/proto/r4/core/resources/observation.pb.h" +#include "proto/google/fhir/proto/r4/core/resources/patient.pb.h" namespace google::fhir { namespace { +using ::google::fhir::r4::core::Observation; +using ::google::fhir::r4::core::Patient; +using ::google::fhir::r4::core::Reference; + void TestToJsonStringValue(std::string input, std::string expected) { absl::StatusOr result = ToJsonStringValue(input); FHIR_ASSERT_OK(result.status()); @@ -54,5 +60,31 @@ TEST(JsonUtilTest, ToJsonStringValueInvalidControlCharactersReturnsError) { EXPECT_FALSE(result.ok()); } +TEST(JsonUtilTest, FhirJsonNameNonReferenceSucceeds) { + EXPECT_EQ( + FhirJsonName(Patient::GetDescriptor()->FindFieldByName("implicit_rules")), + "implicitRules"); +} + +TEST(JsonUtilTest, FhirJsonNameNonReferenceWithExplicitJsonNameSucceeds) { + EXPECT_EQ(FhirJsonName(Observation::Component::ValueX::GetDescriptor() + ->FindFieldByName("string_value")), + "string"); +} + +TEST(JsonUtilTest, FhirJsonNameReferenceUriSucceeds) { + EXPECT_EQ(FhirJsonName(Reference::GetDescriptor()->FindFieldByName("uri")), + "reference"); +} + +TEST(JsonUtilTest, FhirJsonNameReferenceOtherFieldSucceeds) { + EXPECT_EQ( + FhirJsonName(Reference::GetDescriptor()->FindFieldByName("fragment")), + "fragment"); + EXPECT_EQ( + FhirJsonName(Reference::GetDescriptor()->FindFieldByName("patient_id")), + "patientId"); +} + } // namespace } // namespace google::fhir diff --git a/cc/google/fhir/r4/BUILD b/cc/google/fhir/r4/BUILD index dc6732a1b..ca033d4cb 100644 --- a/cc/google/fhir/r4/BUILD +++ b/cc/google/fhir/r4/BUILD @@ -178,13 +178,14 @@ cc_test( ":operation_error_reporter", ":primitive_handler", ":profiles", + ":resource_validation", "//cc/google/fhir:error_reporter", "//cc/google/fhir:proto_util", - "//cc/google/fhir:test_helper", "//cc/google/fhir/json:fhir_json", "//cc/google/fhir/json:json_sax_handler", "//cc/google/fhir/json:test_matchers", "//cc/google/fhir/status", + "//cc/google/fhir/status:statusor", "//cc/google/fhir/testutil:generator", "//cc/google/fhir/testutil:proto_matchers", "//proto/google/fhir/proto:annotations_cc_proto", @@ -339,6 +340,7 @@ cc_test( "//proto/google/fhir/proto/r4/core/resources:verification_result_cc_proto", "//proto/google/fhir/proto/r4/core/resources:vision_prescription_cc_proto", "//testdata/r4/profiles:test_cc_proto", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", diff --git a/cc/google/fhir/r4/json_format_test.cc b/cc/google/fhir/r4/json_format_test.cc index dff2d4427..f52275d52 100644 --- a/cc/google/fhir/r4/json_format_test.cc +++ b/cc/google/fhir/r4/json_format_test.cc @@ -16,7 +16,9 @@ #include "google/fhir/r4/json_format.h" +#include #include +#include #include #include #include @@ -26,8 +28,10 @@ #include "google/protobuf/text_format.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/log/check.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "google/fhir/error_reporter.h" #include "google/fhir/json/fhir_json.h" @@ -37,8 +41,9 @@ #include "google/fhir/r4/operation_error_reporter.h" #include "google/fhir/r4/primitive_handler.h" #include "google/fhir/r4/profiles.h" +#include "google/fhir/r4/resource_validation.h" #include "google/fhir/status/status.h" -#include "google/fhir/test_helper.h" +#include "google/fhir/status/statusor.h" #include "google/fhir/testutil/generator.h" #include "google/fhir/testutil/proto_matchers.h" #include "proto/google/fhir/proto/annotations.pb.h" @@ -213,6 +218,24 @@ using ::testing::UnorderedPointwise; static const char* const kTimeZoneString = "Australia/Sydney"; +inline std::string ReadFile(absl::string_view filename) { + std::ifstream infile; + infile.open( + absl::StrCat(getenv("TEST_SRCDIR"), "/com_google_fhir/", filename)); + + std::ostringstream out; + out << infile.rdbuf(); + return out.str(); +} + +template +T ReadProto(absl::string_view filename) { + T result; + CHECK(google::protobuf::TextFormat::ParseFromString(ReadFile(filename), &result)) + << "Failed to parse proto in file " << filename; + return result; +} + // json_path should be relative to fhir root template absl::StatusOr ParseJsonToProto(const std::string& json_path) { @@ -1710,7 +1733,7 @@ TEST(JsonFormatR4Test, PrintAndParseAllResources) { generator_params.optional_set_ratio_per_level = 0.05; generator_params.max_string_length = 200; auto value_provider = - absl::make_unique( + std::make_unique( generator_params); google::fhir::testutil::FhirGenerator generator( @@ -1929,6 +1952,116 @@ TEST(JsonFormatR4Test, IgnoringRepeatedFieldOrdering(EqualsProto(expected_outcome))); } +TEST(JsonFormatR4Test, ParseRelativeReferenceSucceeds) { + std::string raw_json = R"json( + { + "resourceType": "Observation", + "subject": { + "reference": "Patient/example", + "display": "Amy Shaw" + }})json"; + + OperationOutcome outcome; + OperationOutcomeErrorHandler handler(&outcome); + Observation resource; + absl::Status merge_status = ::google::fhir::r4::MergeJsonFhirStringIntoProto( + raw_json, &resource, absl::LocalTimeZone(), true, handler); + + Observation expected; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(R"pb( + subject { + patient_id { value: "example" } + display { value: "Amy Shaw" } + } + )pb", + &expected)); + + EXPECT_THAT(resource, EqualsProto(expected)); +} + +TEST(JsonFormatR4Test, ParseAbsoluteReferenceSucceeds) { + std::string raw_json = R"json( + { + "resourceType": "Observation", + "subject": { + "reference": "www.patient.org/123", + "display": "Amy Shaw" + }})json"; + + OperationOutcome outcome; + OperationOutcomeErrorHandler handler(&outcome); + Observation resource; + absl::Status merge_status = ::google::fhir::r4::MergeJsonFhirStringIntoProto( + raw_json, &resource, absl::LocalTimeZone(), true, handler); + + Observation expected; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb( + subject { + uri { value: "www.patient.org/123" } + display { value: "Amy Shaw" } + } + )pb", + &expected)); + + EXPECT_THAT(resource, EqualsProto(expected)); +} + +TEST(JsonFormatR4Test, PrintRelativeReferenceSucceeds) { + Observation proto_form; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(R"pb( + subject { + patient_id { value: "example" } + display { value: "Amy Shaw" } + } + )pb", + &proto_form)); + + OperationOutcome outcome; + OperationOutcomeErrorHandler handler(&outcome); + Observation resource; + FHIR_ASSERT_OK_AND_ASSIGN( + std::string json, + ::google::fhir::r4::PrettyPrintFhirToJsonString(proto_form)); + + EXPECT_EQ(json, R"json({ + "resourceType": "Observation", + "subject": { + "reference": "Patient/example", + "display": "Amy Shaw" + } +})json"); +} + +TEST(JsonFormatR4Test, PrintAbsoluteReferenceSucceeds) { + Observation proto_form; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb( + subject { + uri { value: "www.patient.org/123" } + display { value: "Amy Shaw" } + } + )pb", + &proto_form)); + + OperationOutcome outcome; + OperationOutcomeErrorHandler handler(&outcome); + Observation resource; + FHIR_ASSERT_OK_AND_ASSIGN( + std::string json, + ::google::fhir::r4::PrettyPrintFhirToJsonString(proto_form)); + + EXPECT_EQ(json, R"json({ + "resourceType": "Observation", + "subject": { + "reference": "www.patient.org/123", + "display": "Amy Shaw" + } +})json"); +} + } // namespace } // namespace r4 diff --git a/cc/google/fhir/references.cc b/cc/google/fhir/references.cc index 13146e59b..139dbbcec 100644 --- a/cc/google/fhir/references.cc +++ b/cc/google/fhir/references.cc @@ -16,8 +16,21 @@ #include "google/fhir/references.h" +#include +#include + +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" +#include "google/fhir/proto_util.h" +#include "google/fhir/status/status.h" +#include "google/fhir/status/statusor.h" #include "google/fhir/util.h" +#include "re2/re2.h" namespace google { namespace fhir {