From 65dc9e85b730b372b223281b803d3b04839c2886 Mon Sep 17 00:00:00 2001
From: jkv This project has been built with hapi-fhir-jpaserver-example as a base. It has been made more dynamic by replacing There are number of environment variables that will control the behavior of the application at run time (such as start as dstu2 or dstu3 version, whether database schema gets recreated or no, database url, etc..). They can also be defined in Property files, see section below. These are environment variables that can be set before application starts: It can be one of those values: So for example if There are number of property files created for different environments: Any of the Environment variables can be defined in one of the You can run the web application with webapp-runner and pass environment variables to it. Here is a sample command to run the webapp runner which will start dynamic HAPI-FHIR server with version dstu3, postgres database and hibernate schema being dropped and re-created. Note optional command to unset DATABASE_URL, so that only DB_URL is used locally. Also make sure to replace placeholder parameters You should be able to access HAPI_FHIR server at: http://localhost:8080/ . If you'd like to open a debugging port run this command and attach remote debugger in IDE of your choice to port 5000. Again make sure to replace placeholder parameters with actual values before you run the command. Install Tomcat. Make sure you have Tomcat set up in IntelliJ. Add a Run Configuration for running hapi-fhir-jpaserver-dynamic under Tomcat Run the configuration. Point your browser (or fiddler, or what have you) to You should get an empty bundle back. Execute the Use this command to start the container: Note: with this command data is persisted across container restarts, but not after removal of the container. Use a docker volume mapping on /var/lib/jetty/target to achieve this. There is also Dockerfile.tomcat which contains docker commands to run hapi-fhir-jpaserver-dynamic within tomcat. Rename Dockerfile.tomcat to Dockerfile if you would rather use tomcat as your application container.
+ This is the home for the FHIR test server operated by
+ University Health Network. This server
+ (and the testing application you are currently using to access it)
+ is entirely built using
+ HAPI-FHIR,
+ a 100% open-source Java implementation of the
+ FHIR specification.
+
+ Here are some things you might wish to try:
+
+ You are accessing the public FHIR server
+ . This server is hosted elsewhere on the internet
+ but is being accessed using the HAPI client implementation.
+
+
+
+ This is not a production server!
+
+ Do not store any information here that contains personal health information
+ or any other confidential information. This server will be regularly purged
+ and reloaded with fixed test data.
+
+ This schema document describes the XML namespace, in a form
+ suitable for import by other schema documents.
+
+ See
+ http://www.w3.org/XML/1998/namespace.html and
+
+ http://www.w3.org/TR/REC-xml for information
+ about this namespace.
+
+ Note that local names in this namespace are intended to be
+ defined only by the World Wide Web Consortium or its subgroups.
+ The names currently defined in this namespace are listed below.
+ They should not be used with conflicting semantics by any Working
+ Group, specification, or document instance.
+
+ See further below in this document for more information about how to refer to this schema document from your own
+ XSD schema documents and about the
+ namespace-versioning policy governing this schema document.
+
+ denotes an attribute whose value
+ is a language code for the natural language of the content of
+ any element; its value is inherited. This name is reserved
+ by virtue of its definition in the XML specification.
+ Attempting to install the relevant ISO 2- and 3-letter
+ codes as the enumerated possible values is probably never
+ going to be a realistic possibility.
+
+ See BCP 47 at
+ http://www.rfc-editor.org/rfc/bcp/bcp47.txt
+ and the IANA language subtag registry at
+
+ http://www.iana.org/assignments/language-subtag-registry
+ for further information.
+
+ The union allows for the 'un-declaration' of xml:lang with
+ the empty string.
+
+ denotes an attribute whose
+ value is a keyword indicating what whitespace processing
+ discipline is intended for the content of the element; its
+ value is inherited. This name is reserved by virtue of its
+ definition in the XML specification.
+ denotes an attribute whose value
+ provides a URI to be used as the base for interpreting any
+ relative URIs in the scope of the element on which it
+ appears; its value is inherited. This name is reserved
+ by virtue of its definition in the XML Base specification.
+ See http://www.w3.org/TR/xmlbase/
+ for information about this attribute.
+
+ denotes an attribute whose value
+ should be interpreted as if declared to be of type ID.
+ This name is reserved by virtue of its definition in the
+ xml:id specification.
+ See http://www.w3.org/TR/xml-id/
+ for information about this attribute.
+
+ denotes Jon Bosak, the chair of
+ the original XML Working Group. This name is reserved by
+ the following decision of the W3C XML Plenary and
+ XML Coordination groups:
+
+ In appreciation for his vision, leadership and
+ dedication the W3C XML Plenary on this 10th day of
+ February, 2000, reserves for Jon Bosak in perpetuity
+ the XML name "xml:Father".
+
+ This schema defines attributes and an attribute group suitable
+ for use by schemas wishing to allow
+ To enable this, such a schema must import this schema for
+ the XML namespace, e.g. as follows:
+
+ or
+
+ Subsequently, qualified reference to any of the attributes or the
+ group defined below will have the desired effect, e.g.
+
+ will define a type which will schema-validate an instance element
+ with any of those attributes.
+
+ In keeping with the XML Schema WG's standard versioning
+ policy, this schema document will persist at
+
+ http://www.w3.org/2009/01/xml.xsd.
+
+ At the date of issue it can also be found at
+
+ http://www.w3.org/2001/xml.xsd.
+
+ The schema document at that URI may however change in the future,
+ in order to remain compatible with the latest version of XML
+ Schema itself, or with the XML namespace itself. In other words,
+ if the XML Schema or XML namespaces change, the version of this
+ document at
+ http://www.w3.org/2001/xml.xsd
+
+ will change accordingly; the version at
+
+ http://www.w3.org/2009/01/xml.xsd
+
+ will not change.
+
+ Previous dated (and unchanging) versions of this schema
+ document are at:
+
The HAPI FHIR RESTful Server will automatically export a
- conformance statement,
+ capability statement (or a
+ conformance statement for DSTU2 and prior),
as required by the
FHIR Specification.
A selection of Education Levels Generated Narrative with Details id: 180f219f-97a8-486d-99d9-ed631fe4fc57 meta: date: Feb 1, 2013 12:30:02 PM type: Discharge Summary from Responsible Clinician (Details : {LOINC code '28655-9' = 'Physician attending Discharge summary) status: final confidentiality: N author: Doctor Dave. Generated Summary: 23; Adam Careful encounter: http://fhir.healthintersections.com.au/open/Encounter/doc-example Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321 A P TAG
+ Descriptionweb.xml
with ca.uhn.fhir.jpa.demo.WebInitializer
class which extends Spring org.springframework.web.WebApplicationInitializer
class and loads application contexts in a dynamic manner, so that based on environment and/or property variables it can be started either as dstu2 or dstu3 version of HAPI-FHIR JPA Server. Some of the classes have been also refactored to make them more generic.
+ Environment variables
+
+DB_URL
- database url in a standard jdbc url format, specific to a database of your choosing. For example for Postgres it will be: jdbc:postgresql://localhost:5432/<databaseName>?user=<username>&password=<password>
. So far support has been added for MySQL, derby and Postgres databases.DATABASE_URL
- if you deploy your server to HEROKU and create a Postgres database, its URL will be exposed through DATABASE_URL
environment variable set by HEROKU. If DATABASE_URL
is present it will overwrite DB_URL
and its value will be used as jdbc url. This implementations assumes that Heroku will be setup with Postgres database, so current implementation handles postgres DATABASE_URL
that gets set in this format: postgres://<username>:<password>@<hostname>:5432/<databaseName>
. We convert it into standard jdbc format: jdbc:postgresql://localhost:5432/<databaseName>?user=<username>&password=<password>
SCHEMA_NAME
- used only if DATABASE_URL
is set, which is expected to be Postgres database url set by HEROKU. If it's set currentSchema
parameter will be added to the jdbc url, e.g.:
jdbc:postgresql://localhost:5432/<databaseName>?user=<username>&password=<password>¤tScema=<schemaName>
. Note that schema has to be created beforehand and user should have the right permissions to create tables.STU_VERSION
- can be set to dstu2
or dstu3
. If not set by default dstu3
will be used. Corresponding classes will get dynamically loaded at a server startup.ENV
- environment this server will run in, and based on which corresponding property files will be loaded.local, dev, stg, prod
. Based on the value one of the property files will be loaded: resources/config/<STU_VERSION>/app_<ENV>.properties
.ENV=local
and STU_VERSION=dstu3
this file will be loaded:resources/config/dstu3/app_local.properties
HIBERNATE_CREATE
- can be set to true
or false
. If set to true
database schema will be dropped and recreated again upon application startup. If set to false
hibernate will run with validate
as a schema setting.
+ Property fileslocal, dev, stg, prod
. So if ENV
environment variable is set to one of those values corresponding property file will be loaded at a run time, by default local
files will be loaded. Property files are located at: src/main/resources/config/dstu2
and src/main/resources/config/dstu3s
directories. These are the files:
+ app_local.properties
+ app_dev.properties
+ app_stg.properties
+ app_prod.properties
+ immutable.properties - DO NOT modify any of the properties defined in that file.
+
app_<ENV>.properties
property files. If a property is also defined as Environment variable it will overwrite value defined in property file. Properties defined in immutable.properties
should not be changed, those are servlet/Spring mappings and names of classes that will be loaded at a run time and are specific to dstu version being used.
+ Running hapi-fhir-jpaserver-dynamic with a webapp-runner<databasename>
, <username>
and <password>
with actual values.
+mvn clean install
+
+unset DATABASE_URL
+
+java $JAVA_OPTS -DSTU_VERSION=dstu3 -DHIBERNATE_CREATE=true -DDB_URL='jdbc:postgresql://localhost:5432/<databaseName>?user=<username>&password=<password>' -DENV=local -jar target/dependency/webapp-runner.jar target/*.war
+
+java $JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=5000,suspend=n -DSTU_VERSION=dstu3 -DHIBERNATE_CREATE=false -DDB_URL='jdbc:postgresql://localhost:5432/<databaseName>?user=<username>&password=<password>' -DENV=local -jar target/dependency/webapp-runner.jar target/*.war
+
+ Running hapi-fhir-jpaserver-dynamic in Tomcat from IntelliJ
+
+
+
+
+
+http://localhost:8080/fhir/base/Patient
+ Running hapi-fhir-jpaserver-dynamic in a Docker containerbuild-docker-image.sh
script to build the docker image.docker run -d --name hapi-fhir-jpaserver-dynamic -p 8080:8080 hapi-fhir/hapi-fhir-jpaserver-dynamic
+ * config/dstu3/immutable.properties and config/dstu3/app_${ENV}.properties
+ * where ${ENV} is an environment variable named ENV which should be set to one of the strings:
+ * local, dev, stg or prod.
+ *
+ * By default it will be set to local, so config/dstu3/app_local.properties file will be loaded.
+ * It expects properties to be exposed either as as environment variables or through property files. Note that environment variable take precedence over
+ * property files.
+ *
+ *
+ *
+ */
+@Configuration
+@EnableTransactionManagement()
+@PropertySources({
+ @PropertySource("classpath:config/dstu3/immutable.properties"),
+ @PropertySource("classpath:config/dstu3/app_${ENV:local}.properties") })
+public class FhirServerConfig extends BaseJavaConfigDstu3 {
+
+ private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FhirServerConfig.class);
+
+ @Autowired
+ private Environment env;
+ /**
+ * Configure FHIR properties around the the JPA server via this bean
+ */
+ @Bean()
+ public DaoConfig daoConfig() {
+ return FhirServerConfigCommon.getDaoConfig();
+ }
+
+ /**
+ * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a
+ * directory called "jpaserver_derby_files".
+ *
+ * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource.
+ */
+ @Bean(destroyMethod = "close")
+ public DataSource dataSource() {
+ return FhirServerConfigCommon.getDataSource(env);
+ }
+
+ @Bean()
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource());
+ }
+
+ /**
+ * Do some fancy logging to create a nice access log that has details about each incoming request.
+ */
+ public IServerInterceptor loggingInterceptor() {
+ return FhirServerConfigCommon.loggingInterceptor();
+ }
+
+ /**
+ * This interceptor adds some pretty syntax highlighting in responses when a browser is detected
+ */
+ @Bean(autowire = Autowire.BY_TYPE)
+ public IServerInterceptor responseHighlighterInterceptor() {
+ return FhirServerConfigCommon.getResponseHighlighterInterceptor();
+ }
+
+ @Bean(autowire = Autowire.BY_TYPE)
+ public IServerInterceptor subscriptionSecurityInterceptor() {
+ String stuVersion = (env.getProperty(Utils.STU_VERSION) == null)?Utils.DSTU3:env.getProperty(Utils.STU_VERSION) ;
+ logger.info("-------STU_VERSION: " + stuVersion);
+ SubscriptionsRequireManualActivationInterceptorDstu3 interceptor = new SubscriptionsRequireManualActivationInterceptorDstu3();
+ return interceptor;
+ }
+
+ @Bean()
+ public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
+ return FhirServerConfigCommon.getTransactionManager(entityManagerFactory);
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java
new file mode 100644
index 00000000000..1015ca79ffe
--- /dev/null
+++ b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigCommon.java
@@ -0,0 +1,165 @@
+package ca.uhn.fhir.jpa.demo;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.commons.lang3.time.DateUtils;
+import org.hibernate.jpa.HibernatePersistenceProvider;
+import org.springframework.core.env.Environment;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
+
+
+/**
+ * Common code for dstu2 and dstu3 classes moved into static methods so that they can be called from version specific class.
+ *
+ * @author anoushmouradian
+ *
+ */
+public class FhirServerConfigCommon {
+
+ private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class);
+
+ /**
+ * Configure FHIR properties around the the JPA server via this bean
+ */
+ @SuppressWarnings("deprecation")
+ public static DaoConfig getDaoConfig() {
+ DaoConfig daoConfig = new DaoConfig();
+ daoConfig.setSubscriptionEnabled(true);
+ daoConfig.setSubscriptionPollDelay(5000);
+ daoConfig.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
+ daoConfig.setAllowMultipleDelete(true);
+ return daoConfig;
+ }
+
+ /**
+ * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a
+ * directory called "jpaserver_derby_files".
+ *
+ * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource.
+ */
+ public static DataSource getDataSource(Environment env) {
+ String dbUrl = (env.getProperty(Utils.DB_URL) != null)?env.getProperty(Utils.DB_URL).toLowerCase():"";
+ String herokuDbUrl = env.getProperty(Utils.HEROKU_DATABASE_URL);
+
+ if(herokuDbUrl != null) {
+ // url will come as: postgres://user:pass@host:5432/dbname
+ String fromUserName = herokuDbUrl.substring(herokuDbUrl.indexOf("//") + 2);
+ String userName = fromUserName.substring(0, fromUserName.indexOf(":"));
+ String pass = fromUserName.substring(fromUserName.indexOf(":") +1, fromUserName.indexOf("@"));
+ String fromHost = fromUserName.substring(fromUserName.indexOf("@")+1);
+ String schemaName = env.getProperty(Utils.SCHEMA_NAME);
+
+ //build this url: jdbc:postgresql://host:5432/dbName?user=username&password=pass¤tSchema='
+ dbUrl = "jdbc:postgresql://" + fromHost + "?user=" + userName + "&password=" + pass + "&sslmode=require";
+ if(schemaName != null) {
+ dbUrl += "¤tSchema=" + schemaName;
+ }
+ logger.info("------DB Url: " + dbUrl);
+ }
+ BasicDataSource dataSource = new BasicDataSource();
+ try {
+ if(dbUrl.indexOf("mysql") > -1 ) {
+ dataSource.setDriver(new com.mysql.jdbc.Driver());
+ } else if(dbUrl.indexOf("postgres") > -1) {
+ dataSource.setDriver(new org.postgresql.Driver());
+ } else if(dbUrl.indexOf("derby") > -1) {
+ dataSource.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
+ }
+ } catch (SQLException e) {
+ logger.error("----FhiServerConfigCommon: getDataSource: setting driver error: " + e.getMessage());
+ }
+ dataSource.setUrl(dbUrl);
+ return dataSource;
+ }
+
+ public static LocalContainerEntityManagerFactoryBean getEntityManagerFactory(Environment env, DataSource dataSource) {
+ LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
+ retVal.setPersistenceUnitName("HAPI_PU");
+ retVal.setDataSource(dataSource);
+
+ retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
+ retVal.setPersistenceProvider(new HibernatePersistenceProvider());
+ retVal.setJpaProperties(jpaProperties(env));
+ return retVal;
+ }
+
+
+ /**
+ * Do some fancy logging to create a nice access log that has details about each incoming request.
+ */
+ public static IServerInterceptor loggingInterceptor() {
+ LoggingInterceptor retVal = new LoggingInterceptor();
+ retVal.setLoggerName("fhirtest.access");
+ retVal.setMessageFormat(
+ "Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]");
+ retVal.setLogExceptions(true);
+ retVal.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}");
+ return retVal;
+ }
+
+ /**
+ * This interceptor adds some pretty syntax highlighting in responses when a browser is detected
+ */
+ public static IServerInterceptor getResponseHighlighterInterceptor() {
+ ResponseHighlighterInterceptor retVal = new ResponseHighlighterInterceptor();
+ return retVal;
+ }
+
+ public static JpaTransactionManager getTransactionManager(EntityManagerFactory entityManagerFactory) {
+ JpaTransactionManager retVal = new JpaTransactionManager();
+ retVal.setEntityManagerFactory(entityManagerFactory);
+ return retVal;
+ }
+
+ private static Properties jpaProperties(Environment env) {
+ Properties extraProperties = new Properties();
+ String dbUrl = (env.getProperty(Utils.HEROKU_DATABASE_URL) == null)?env.getProperty(Utils.DB_URL):env.getProperty(Utils.HEROKU_DATABASE_URL);
+ if(dbUrl != null && dbUrl.indexOf("mysql") > -1) {
+ extraProperties.put("hibernate.dialect", org.hibernate.dialect.MySQL5Dialect.class.getName());
+ extraProperties.put("hibernate.dialect.storage_engine","innodb");
+ }
+ else if(dbUrl != null && dbUrl.indexOf("postgres") > -1) {
+ extraProperties.put("hibernate.dialect", org.hibernate.dialect.PostgreSQL9Dialect.class.getName());
+ }
+ else if(dbUrl != null && dbUrl.indexOf("derby") > -1) {
+ extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName());
+ }
+ boolean hibernateCreate = new Boolean(env.getProperty(Utils.HIBERNATE_CREATE));
+ logger.info("------DB hibernateCreate: " + hibernateCreate);
+ if(hibernateCreate){
+ extraProperties.put("hibernate.hbm2ddl.auto", "create");
+ } else {
+ extraProperties.put("hibernate.hbm2ddl.auto", "validate");
+ }
+ extraProperties.put("hibernate.format_sql", "true");
+ extraProperties.put("hibernate.show_sql", "false");
+
+ extraProperties.put("hibernate.jdbc.batch_size", "20");
+ extraProperties.put("hibernate.cache.use_query_cache", "false");
+ extraProperties.put("hibernate.cache.use_second_level_cache", "false");
+ extraProperties.put("hibernate.cache.use_structured_entries", "false");
+ extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
+ extraProperties.put("hibernate.search.autoregister_listeners", "false");
+ extraProperties.put("hibernate.search.default.indexBase", "./target/lucenefiles");
+ extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
+ extraProperties.put("hibernate.search.indexing_strategy", "manual");
+ extraProperties.put("hibernate.search.default.worker.execution", "async");
+
+ return extraProperties;
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
new file mode 100644
index 00000000000..982f2ab193d
--- /dev/null
+++ b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
@@ -0,0 +1,110 @@
+package ca.uhn.fhir.jpa.demo;
+
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowire;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.core.env.Environment;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+
+
+/**
+ * This is the primary configuration file for the dynamic jpa server running with dstu2 version.
+ * It will load 2 property files:
+ * config/dstu2/immutable.properties and config/dstu2/app_${ENV}.properties
+ * where ${ENV} is an environment variable named ENV which should be set to one of the strings:
+ * local, dev, stg or prod.
+ *
+ * By default it will be set to local, so config/dstu2/app_local.properties file will be loaded.
+ * It expects properties to be exposed either as as environment variables or through property files. Note that environment variable take precedence over
+ * property files.
+ *
+ *
+ *
+ */
+@Configuration
+@EnableTransactionManagement()
+@PropertySources({
+ @PropertySource("classpath:config/dstu2/immutable.properties"),
+ @PropertySource("classpath:config/dstu2/app_${ENV:local}.properties") })
+public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
+
+ private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FhirServerConfigDstu2.class);
+
+ @Autowired
+ private Environment env;
+
+ /**
+ * Configure FHIR properties around the the JPA server via this bean
+ */
+ @SuppressWarnings("deprecation")
+ @Bean()
+ public DaoConfig daoConfig() {
+ return FhirServerConfigCommon.getDaoConfig();
+ }
+
+ /**
+ * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a
+ * directory called "jpaserver_derby_files".
+ *
+ * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource.
+ */
+ @Bean(destroyMethod = "close")
+ public DataSource dataSource() {
+ return FhirServerConfigCommon.getDataSource(env);
+ }
+
+ @Bean()
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource());
+ }
+
+
+ /**
+ * Do some fancy logging to create a nice access log that has details about each incoming request.
+ */
+ public IServerInterceptor loggingInterceptor() {
+ return FhirServerConfigCommon.loggingInterceptor();
+ }
+
+ /**
+ * This interceptor adds some pretty syntax highlighting in responses when a browser is detected
+ */
+ @Bean(autowire = Autowire.BY_TYPE)
+ public IServerInterceptor responseHighlighterInterceptor() {
+ return FhirServerConfigCommon.getResponseHighlighterInterceptor();
+ }
+
+ @Bean(autowire = Autowire.BY_TYPE)
+ public IServerInterceptor subscriptionSecurityInterceptor() {
+ String stuVersion = (env.getProperty(Utils.STU_VERSION) == null)?Utils.DSTU2:env.getProperty(Utils.STU_VERSION) ;
+ logger.info("-------STU_VERSION: " + stuVersion);
+ SubscriptionsRequireManualActivationInterceptorDstu2 interceptor = new SubscriptionsRequireManualActivationInterceptorDstu2();
+ return interceptor;
+ }
+
+ @Bean()
+ public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
+ return FhirServerConfigCommon.getTransactionManager(entityManagerFactory);
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfig.java b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfig.java
new file mode 100644
index 00000000000..f5eb07c4242
--- /dev/null
+++ b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfig.java
@@ -0,0 +1,68 @@
+package ca.uhn.fhir.jpa.demo;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.core.env.Environment;
+
+import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.to.FhirTesterMvcConfig;
+import ca.uhn.fhir.to.TesterConfig;
+
+//@formatter:off
+/**
+ * This spring config file configures the web testing module. It serves two
+ * purposes:
+ * 1. It imports FhirTesterMvcConfig, which is the spring config for the
+ * tester itself
+ * 2. It tells the tester which server(s) to talk to, via the testerConfig()
+ * method below.
+ * It will also load properties defined in config/dstu3/immutable.properties file.
+ */
+@Configuration
+@PropertySources({
+ @PropertySource("classpath:config/dstu3/immutable.properties") })
+@Import(FhirTesterMvcConfig.class)
+public class FhirTesterConfig {
+
+ @Autowired
+ private Environment env;
+
+ /**
+ * This bean tells the testing webpage which servers it should configure itself
+ * to communicate with. In this example we configure it to talk to the local
+ * server, as well as one public server. If you are creating a project to
+ * deploy somewhere else, you might choose to only put your own server's
+ * address here.
+ *
+ * Note the use of the ${serverBase} variable below. This will be replaced with
+ * the base URL as reported by the server itself. Often for a simple Tomcat
+ * (or other container) installation, this will end up being something
+ * like "http://localhost:8080/hapi-fhir-jpaserver-example". If you are
+ * deploying your server to a place with a fully qualified domain name,
+ * you might want to use that instead of using the variable.
+ */
+ @Bean
+ public TesterConfig testerConfig() {
+ TesterConfig retVal = new TesterConfig();
+ String baseFhirMapping = env.getProperty(Utils.BASE_FHIR_MAPPING);
+ baseFhirMapping = (baseFhirMapping == null)?"fhir":baseFhirMapping;
+ retVal
+ .addServer()
+ .withId("home")
+ .withFhirVersion(FhirVersionEnum.DSTU3)
+ .withBaseUrl("${serverBase}/" + baseFhirMapping)
+ .withName("Local Tester")
+ .addServer()
+ .withId("hapi")
+ .withFhirVersion(FhirVersionEnum.DSTU3)
+ .withBaseUrl("http://fhirtest.uhn.ca/" + baseFhirMapping)
+ .withName("Public HAPI Test Server");
+ return retVal;
+ }
+
+}
+//@formatter:on
diff --git a/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfigDstu2.java b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfigDstu2.java
new file mode 100644
index 00000000000..fb2822436b3
--- /dev/null
+++ b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfigDstu2.java
@@ -0,0 +1,71 @@
+package ca.uhn.fhir.jpa.demo;
+
+import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.to.FhirTesterMvcConfig;
+import ca.uhn.fhir.to.TesterConfig;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.core.env.Environment;
+
+//@formatter:off
+
+/**
+ * This spring config file configures the web testing module. It serves two
+ * purposes:
+ * 1. It imports FhirTesterMvcConfig, which is the spring config for the
+ * tester itself
+ * 2. It tells the tester which server(s) to talk to, via the testerConfig()
+ * method below
+ * It will also load properties defined in config/dstu2/immutable.properties file.
+ */
+@Configuration
+@PropertySources({
+ @PropertySource("classpath:config/dstu2/immutable.properties") })
+@Import(FhirTesterMvcConfig.class)
+public class FhirTesterConfigDstu2 {
+ private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FhirTesterConfigDstu2.class);
+
+ @Autowired
+ private Environment env;
+
+ /**
+ * This bean tells the testing webpage which servers it should configure itself
+ * to communicate with. In this example we configure it to talk to the local
+ * server, as well as one public server. If you are creating a project to
+ * deploy somewhere else, you might choose to only put your own server's
+ * address here.
+ *
+ * Note the use of the ${serverBase} variable below. This will be replaced with
+ * the base URL as reported by the server itself. Often for a simple Tomcat
+ * (or other container) installation, this will end up being something
+ * like "http://localhost:8080/hapi-fhir-jpaserver-example". If you are
+ * deploying your server to a place with a fully qualified domain name,
+ * you might want to use that instead of using the variable.
+ */
+ @Bean
+ public TesterConfig testerConfig() {
+ logger.info("-------FhirTesterConfigDstu2:" + "testerConfig");
+ TesterConfig retVal = new TesterConfig();
+ String baseFhirMapping = env.getProperty(Utils.BASE_FHIR_MAPPING);
+ baseFhirMapping = (baseFhirMapping == null)?"fhir":baseFhirMapping;
+ retVal
+ .addServer()
+ .withId("home")
+ .withFhirVersion(FhirVersionEnum.DSTU2)
+ .withBaseUrl("${serverBase}/" + baseFhirMapping)
+ .withName("Local Tester")
+ .addServer()
+ .withId("hapi")
+ .withFhirVersion(FhirVersionEnum.DSTU2)
+ .withBaseUrl("http://fhirtest.uhn.ca/" + baseFhirMapping)
+ .withName("Public HAPI Test Server");
+ return retVal;
+ }
+
+}
+//@formatter:on
diff --git a/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/ImmutablePropertiesConfig.java b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/ImmutablePropertiesConfig.java
new file mode 100644
index 00000000000..b625bcf4b19
--- /dev/null
+++ b/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/ImmutablePropertiesConfig.java
@@ -0,0 +1,64 @@
+package ca.uhn.fhir.jpa.demo;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+/**
+ * Load immutable properties from /resources/config/
+
+ DataSource
properties
+ such as serverName
, databaseName
+ and portNumber
, the more specific properties will
+ take precedence and url
will be ignored.
+
+ ]]>
+ About the XML namespace
+
+ lang (as an attribute name)
+ Notes
+ space (as an attribute name)
+ base (as an attribute name)
+ id (as an attribute name)
+ Father (in any context at all)
+
+
+
+ About this schema document
+
+ xml:base
,
+ xml:lang
, xml:space
or
+ xml:id
attributes on elements they define.
+
+ <schema . . .>
+ . . .
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+
+
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+
+
+ <type . . .>
+ . . .
+ <attributeGroup ref="xml:specialAttrs"/>
+
+ Versioning policy for this schema document
+ Education Levels
http://csiro.au/ValueSet/education-levels",
+ "
",
+ "
",
+ "
",
+ "
",
+ "
",
+ "
",
+ "
", "
", "", "
", "
", "", "
", "
", "", "
line1\nline2\nline3 BOLD
", + "
line1\nline2\nline3 BOLD" + )); + //@formatter:on + + } + + @Test + public void testEncodeDivWithPrePrettyPrint() { + + Patient p = new Patient(); + p.getText().setDivAsString("
A P TAG
line1\nline2\nline3 BOLD
", ""));
+ assertThat(encoded, not(containsString("text")));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, containsString("maritalStatus"));
+ }
+
+ @Test
+ public void testEncodeNonContained() {
+ // Create an organization
+ Organization org = new Organization();
+ org.setId("Organization/65546");
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+ patient.getManagingOrganization().setResource(org);
+
+ // Create a list containing both resources. In a server method, you might just
+ // return this list, but here we will create a bundle to encode.
+ List
", ""));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeSummary2() {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("", ""));
+ assertThat(encoded, stringContainsInOrder("", " ", "
", " "));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeWithContained() {
+ List contained = new ArrayList();
+
+ // Will be added by reference
+ Patient p = new Patient();
+ p.setId("#" + "1000");
+ contained.add(p);
+
+ // Will be added by direct resource object
+ Location l = new Location();
+ l.setId("#" + "1001");
+ contained.add(l);
+
+ // Will not be referred to (and therefore shouldn't appear in output)
+ Location l2 = new Location();
+ l2.setId("#1002");
+ contained.add(l2);
+
+ Appointment appointment = new Appointment();
+ appointment.setId("1234");
+ appointment.getContained().addAll(contained);
+
+ appointment.addParticipant().getActor().setReference("#1000");
+ appointment.addParticipant().getActor().setResource(l);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(appointment);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ " "
+ ));
+ //@formatter:on
+
+ assertThat(encoded, not(containsString("#1002")));
+ }
+
+ @Test
+ public void testEncodeWithDontEncodeElements() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("123");
+ patient.getMeta().addProfile("http://profile");
+ patient.addName().addFamily("FAMILY").addGiven("GIVEN");
+ patient.addAddress().addLine("LINE1");
+
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("*.meta", "*.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.meta", "Patient.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.name.family"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("GIVEN"));
+ assertThat(out, not(containsString("FAMILY")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("*.meta", "*.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.meta"));
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient.name")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("id"));
+ assertThat(out, not(containsString("address")));
+ assertThat(out, not(containsString("meta")));
+ }
+ }
+
+ @Test
+ public void testEncodeWithEncodeElements() throws Exception {
+ Patient patient = new Patient();
+ patient.getMeta().addProfile("http://profile");
+ patient.addName().addFamily("FAMILY");
+ patient.addAddress().addLine("LINE1");
+
+ Bundle bundle = new Bundle();
+ bundle.setTotal(100);
+ bundle.addEntry().setResource(patient);
+
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient.name", "Bundle.entry")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, not(containsString("total")));
+ assertThat(out, (containsString("Patient")));
+ assertThat(out, (containsString("name")));
+ assertThat(out, not(containsString("address")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient.name")));
+ p.setEncodeElementsAppliesToResourceTypes(new HashSet(Arrays.asList("Patient")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, (containsString("total")));
+ assertThat(out, (containsString("Patient")));
+ assertThat(out, (containsString("name")));
+ assertThat(out, not(containsString("address")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient")));
+ p.setEncodeElementsAppliesToResourceTypes(new HashSet(Arrays.asList("Patient")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, (containsString("total")));
+ assertThat(out, (containsString("Patient")));
+ assertThat(out, (containsString("name")));
+ assertThat(out, (containsString("address")));
+ }
+
+ }
+
+ @Test
+ public void testMoreExtensions() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setUse(Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ Extension ext = new Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
+
+ // Add the extension to the resource
+ patient.addExtension(ext);
+ // END SNIPPET: resourceExtension
+
+ // START SNIPPET: resourceStringExtension
+ HumanName name = patient.addName();
+ name.addFamily("Shmoe");
+ StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given"));
+ given.addExtension(ext2);
+
+ StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent");
+ given2.addExtension(given2ext);
+ Extension givenExtChild = new Extension();
+ givenExtChild.setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD"));
+ given2ext.addExtension(givenExtChild);
+ // END SNIPPET: resourceStringExtension
+
+ // START SNIPPET: subExtension
+ Extension parent = new Extension().setUrl("http://example.com#parent");
+ patient.addExtension(parent);
+
+ Extension child1 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.addExtension(child1);
+
+ Extension child2 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.addExtension(child2);
+ // END SNIPPET: subExtension
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(
+ " "));
+ }
+
+ @Test
+ public void testOmitResourceId() {
+ Patient p = new Patient();
+ p.setId("123");
+ p.addName().addFamily("ABC");
+
+ assertThat(ourCtx.newXmlParser().encodeResourceToString(p), stringContainsInOrder("123", "ABC"));
+ assertThat(ourCtx.newXmlParser().setOmitResourceId(true).encodeResourceToString(p), containsString("ABC"));
+ assertThat(ourCtx.newXmlParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
+ }
+
+
+ @Test
+ public void testParseAndEncodeComments() throws IOException {
+ //@formatter:off
+ String input = "\n" +
+ " " +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321
\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " " +
+ " " +
+ " ";
+ //@formatter:off
+
+ Patient res = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ res.getFormatCommentsPre();
+ assertEquals("Patient/pat1", res.getId());
+ assertEquals("654321", res.getIdentifier().get(0).getValue());
+ assertEquals(true, res.getActive());
+
+ assertThat(res.getIdElement().getFormatCommentsPre(), contains("pre resource comment"));
+ assertThat(res.getIdentifier().get(0).getFormatCommentsPre(), contains("identifier comment 1", "identifier comment 2"));
+ assertThat(res.getIdentifier().get(0).getUseElement().getFormatCommentsPre(), contains("use comment 1", "use comment 2"));
+ assertThat(res.getActiveElement().getFormatCommentsPost(), contains("post resource comment"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(res);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "\"identifier\": [",
+ "{",
+ "\"fhir_comments\":",
+ "[",
+ "\"identifier comment 1\"",
+ ",",
+ "\"identifier comment 2\"",
+ "]",
+ "\"use\": \"usual\",",
+ "\"_use\": {",
+ "\"fhir_comments\":",
+ "[",
+ "\"use comment 1\"",
+ ",",
+ "\"use comment 2\"",
+ "]",
+ "},",
+ "\"type\""
+ ));
+ //@formatter:off
+
+ encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ " ",
+ "Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321
",
+ "",
+ " ",
+ " \n",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ "",
+ " "
+ ));
+ //@formatter:off
+
+ }
+
+ @Test
+ public void testParseAndEncodeCommentsOnExtensions() {
+ //@formatter:off
+ String input =
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Patient pat = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pat);
+ ourLog.info(output);
+
+ assertThat(output, stringContainsInOrder(
+ "",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+ ));
+
+ output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(pat);
+ ourLog.info(output);
+
+ assertThat(output, stringContainsInOrder(
+ "{",
+ " \"resourceType\": \"Patient\",",
+ " \"id\": \"someid\",",
+ " \"_id\": {",
+ " \"fhir_comments\": [",
+ " \" comment 1 \"",
+ " ]",
+ " },",
+ " \"extension\": [",
+ " {",
+ " \"fhir_comments\": [",
+ " \" comment 2 \",",
+ " \" comment 7 \"",
+ " ],",
+ " \"url\": \"urn:patientext:att\",",
+ " \"valueAttachment\": {",
+ " \"fhir_comments\": [",
+ " \" comment 3 \",",
+ " \" comment 6 \"",
+ " ],",
+ " \"contentType\": \"aaaa\",",
+ " \"_contentType\": {",
+ " \"fhir_comments\": [",
+ " \" comment 4 \"",
+ " ]",
+ " },",
+ " \"data\": \"AAAA\",",
+ " \"_data\": {",
+ " \"fhir_comments\": [",
+ " \" comment 5 \"",
+ " ]",
+ " }",
+ " }",
+ " }",
+ " ]",
+ "}"
+ ));
+
+ //@formatter:on
+ }
+
+
+ @Test
+ public void testParseAndEncodeNestedExtensions() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+
+ Patient p = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ DateType bd = p.getBirthDateElement();
+ assertEquals("2005-03-04", bd.getValueAsString());
+
+ List exts = bd.getExtensionsByUrl("http://my.fancy.extension.url");
+ assertEquals(1, exts.size());
+ Extension ext = exts.get(0);
+ assertEquals(null, ext.getValue());
+
+ exts = ext.getExtensionsByUrl("http://my.fancy.extension.url");
+ assertEquals(1, exts.size());
+ ext = exts.get(0);
+ assertEquals("myNestedValue", ((StringType) ext.getValue()).getValue());
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ " ",
+ " "));
+ //@formatter:on
+
+ }
+
+ @Test
+ public void testParseBundleNewWithPlaceholderIds() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+ //@formatter:on
+
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:oid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+
+ }
+
+ @Test
+ public void testParseBundleNewWithPlaceholderIdsInBase1() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+ //@formatter:on
+
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:oid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+ }
+
+ @Test
+ public void testParseBundleNewWithPlaceholderIdsInBase2() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+ //@formatter:on
+
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:uuid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+
+ //@formatter:off
+ input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+ //@formatter:on
+
+ parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:uuid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+
+ }
+
+ @Test
+ public void testParseBundleOldStyleWithUnknownLinks() throws Exception {
+ //@formatter:off
+ String bundle = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+
+ Bundle b = (Bundle) ourCtx.newXmlParser().parseResource(bundle);
+ assertEquals(1, b.getEntry().size());
+
+ }
+
+ @Test
+ public void testParseBundleOldWithPlaceholderIds() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+ //@formatter:on
+
+ Bundle parsed = (Bundle) ourCtx.newXmlParser().parseResource(input);
+ assertEquals("urn:oid:0.1.2.3", parsed.getEntry().get(0).getResource().getId());
+
+ }
+
+ @Test
+ public void testParseBundleWithBinary() {
+ // TODO: implement this test, make sure we handle ID and meta correctly in Binary
+ }
+
+
+
+ @Test
+ public void testParseBundleWithResourceId() {
+ //@formatter:off
+ String input = ""
+ + " "
+ + " "
+ + " "
+ + " \n";
+ //@formatter:on
+
+ Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/3", bundle.getEntry().get(0).getResource().getIdElement().getValue());
+ assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/2", bundle.getEntry().get(1).getResource().getIdElement().getValue());
+ assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/1", bundle.getEntry().get(2).getResource().getIdElement().getValue());
+ }
+
+ /**
+ * Thanks to Alexander Kley!
+ */
+ @Test
+ public void testParseContainedBinaryResource() {
+ byte[] bin = new byte[] { 0, 1, 2, 3, 4 };
+ final Binary binary = new Binary();
+ binary.setContentType("PatientConsent").setContent(bin);
+
+ DocumentManifest manifest = new DocumentManifest();
+ CodeableConcept cc = new CodeableConcept();
+ cc.addCoding().setSystem("mySystem").setCode("PatientDocument");
+ manifest.setType(cc);
+ manifest.setMasterIdentifier(new Identifier().setSystem("mySystem").setValue(UUID.randomUUID().toString()));
+ manifest.addContent().setP(new Reference(binary));
+ manifest.setStatus(Enumerations.DocumentReferenceStatus.CURRENT);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(manifest);
+ ourLog.info(encoded);
+ assertThat(encoded, StringContainsInOrder.stringContainsInOrder(Arrays.asList("contained>", "")));
+
+ DocumentManifest actual = ourCtx.newXmlParser().parseResource(DocumentManifest.class, encoded);
+ assertEquals(1, actual.getContained().size());
+ assertEquals(1, actual.getContent().size());
+
+ /*
+ * If this fails, it's possibe the DocumentManifest structure is wrong: It should be
+ *
+ * @Child(name = "p", type = {Attachment.class, ValueSet.class}, order=1, min=1, max=1, modifier=false, summary=true)
+ */
+ assertNotNull(((Reference) actual.getContent().get(0).getP()).getResource());
+ }
+
+ /**
+ * See #426
+ */
+ @Test
+ public void testParseExtensionWithIdType() {
+ //@formatter:off
+ String input =
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+ Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input);
+
+ List extList = pt.getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare");
+ extList = extList.get(0).getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare-aaa-id");
+ Extension ext = extList.get(0);
+ IdType value = (IdType) ext.getValue();
+ assertEquals("mc1", value.getValueAsString());
+ }
+
+ /**
+ * See #426
+ *
+ * Value type of FOO isn't a valid datatype
+ */
+ @Test
+ public void testParseExtensionWithInvalidType() {
+ //@formatter:off
+ String input =
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+ Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input);
+
+ List extList = pt.getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare");
+ extList = extList.get(0).getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare-aaa-id");
+ Extension ext = extList.get(0);
+ IdType value = (IdType) ext.getValue();
+ assertEquals(null, value);
+ }
+
+ /**
+ * See #342
+ */
+ @Test(expected = DataFormatException.class)
+ public void testParseInvalid() {
+ ourCtx.newXmlParser().parseResource("FOO");
+ }
+
+ /**
+ * See #366
+ */
+ @Test()
+ public void testParseInvalidBoolean() {
+ //@formatter:off
+ String resource = "\n" +
+ " \n" +
+ " ";
+ //@formatter:on
+
+ IParser p = ourCtx.newXmlParser();
+
+ try {
+ p.parseResource(resource);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"1\": Invalid boolean string: '1'", e.getMessage());
+ }
+
+ LenientErrorHandler errorHandler = new LenientErrorHandler();
+ assertEquals(true, errorHandler.isErrorOnInvalidValue());
+ errorHandler.setErrorOnInvalidValue(false);
+ p.setParserErrorHandler(errorHandler);
+ }
+
+ @Test
+ public void testParseInvalidTextualNumber() {
+ Observation obs = new Observation();
+ obs.setValue(new Quantity().setValue(1234));
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs);
+ encoded = encoded.replace("1234", "\"1234\"");
+ ourLog.info(encoded);
+ ourCtx.newJsonParser().parseResource(encoded);
+ }
+
+ @Test
+ public void testParseMetadata() throws Exception {
+ //@formatter:off
+ String bundle = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+
+ Bundle b = ourCtx.newXmlParser().parseResource(Bundle.class, bundle);
+ assertEquals(1, b.getEntry().size());
+
+ Bundle.BundleEntryComponent entry = b.getEntry().get(0);
+ Patient pt = (Patient) entry.getResource();
+ assertEquals("http://foo/fhirBase2/Patient/1/_history/2", pt.getIdElement().getValue());
+ assertEquals("2012-01-02", pt.getBirthDateElement().getValueAsString());
+ assertEquals("0.123", entry.getSearch().getScore().toString());
+ assertEquals("match", entry.getSearch().getMode().toCode());
+ assertEquals("POST", entry.getRequest().getMethod().toCode());
+ assertEquals("http://foo/Patient?identifier=value", entry.getRequest().getUrl());
+ assertEquals("2001-02-22T09:22:33-07:00", pt.getMeta().getLastUpdatedElement().getValueAsString());
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String reEncoded = p.encodeResourceToString(b);
+ ourLog.info(reEncoded);
+
+ compareXml(bundle, reEncoded);
+
+ }
+
+ @Test
+ public void testParseMetaUpdatedDate() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+ Bundle b = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+
+ InstantType updated = b.getMeta().getLastUpdatedElement();
+ assertEquals("2015-06-22T15:48:57.554-04:00", updated.getValueAsString());
+
+ }
+
+ @Test
+ public void testParseNestedExtensionsInvalid() {
+ //@formatter:off
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+ //@formatter:on
+
+ try {
+ ourCtx.newXmlParser().parseResource(Patient.class, input);
+ fail();
+ } catch (DataFormatException e) {
+ assertThat(e.getMessage(), containsString("Extension (URL='http://my.fancy.extension.url') must not have both a value and other contained extensions"));
+ }
+ }
+
+ /**
+ * See #163
+ */
+ @Test
+ public void testParseResourceType() {
+ IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ // Patient
+ Patient patient = new Patient();
+ String patientId = UUID.randomUUID().toString();
+ patient.setId(new IdType("Patient", patientId));
+ patient.addName().addGiven("John").addFamily("Smith");
+ patient.setGender(Enumerations.AdministrativeGender.MALE);
+ patient.setBirthDateElement(new DateType("1987-04-16"));
+
+ // Bundle
+ Bundle bundle = new Bundle();
+ bundle.setType(Bundle.BundleType.COLLECTION);
+ bundle.addEntry().setResource(patient);
+
+ String bundleText = xmlParser.encodeResourceToString(bundle);
+ ourLog.info(bundleText);
+
+ Bundle reincarnatedBundle = xmlParser.parseResource(Bundle.class, bundleText);
+ Patient reincarnatedPatient = (Patient) reincarnatedBundle.getEntry().get(0).getResource();
+
+ assertEquals("Patient", patient.getIdElement().getResourceType());
+ assertEquals("Patient", reincarnatedPatient.getIdElement().getResourceType());
+ }
+
+ /**
+ * See #344
+ */
+ @Test
+ public void testParserIsCaseSensitive() {
+ Observation obs = new Observation();
+ SampledData data = new SampledData();
+ data.setData("1 2 3");
+ data.setOrigin((SimpleQuantity) new SimpleQuantity().setValue(0L));
+ data.setPeriod(1000L);
+ obs.setValue(data);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true).setParserErrorHandler(new StrictErrorHandler());
+ String encoded = p.encodeResourceToString(obs);
+ ourLog.info(encoded);
+
+ p.parseResource(encoded);
+
+ try {
+ p.parseResource(encoded.replace("Observation", "observation"));
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [1,1]]: Unknown resource type 'observation': Resource names are case sensitive, found similar name: 'Observation'",
+ e.getMessage());
+ }
+
+ try {
+ p.parseResource(encoded.replace("valueSampledData", "valueSampleddata"));
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Unknown element 'valueSampleddata' found during parse", e.getMessage());
+ }
+ }
+
+ /**
+ * See #339
+ *
+ * https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
+ */
+ @Test
+ public void testXxe() {
+ //@formatter:off
+ String input =
+ "" +
+ "" +
+ "]>" +
+ "" +
+ "" +
+ "TEXT &xxe; TEXT" +
+ " " +
+ "" +
+ "" +
+ " " +
+ " ";
+ //@formatter:on
+
+ ourLog.info(input);
+
+ try {
+ ourCtx.newXmlParser().parseResource(Patient.class, input);
+ fail();
+ } catch (DataFormatException e) {
+ assertThat(e.toString(), containsString("Undeclared general entity"));
+ }
+
+ }
+
+ @Test
+ public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
+ String refVal = "http://my.org/FooBar";
+
+ Patient fhirPat = new Patient();
+ fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
+
+ IParser parser = ourCtx.newXmlParser();
+
+ String output = parser.encodeResourceToString(fhirPat);
+ System.out.println("output: " + output);
+
+ // Deserialize then check that valueReference value is still correct
+ fhirPat = parser.parseResource(Patient.class, output);
+
+ List extlst = fhirPat.getExtensionsByUrl("x1");
+ Assert.assertEquals(1, extlst.size());
+ Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
+ }
+
+ public static void compareXml(String content, String reEncoded) {
+ Diff d = DiffBuilder.compare(Input.fromString(content))
+ .withTest(Input.fromString(reEncoded))
+ .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .checkForSimilar()
+ .ignoreWhitespace()
+ .ignoreComments()
+ .withComparisonController(ComparisonControllers.Default)
+ .build();
+
+ assertTrue(d.toString(), !d.hasDifferences());
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class TestPatientFor327 extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "testCondition")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "testCondition", definedLocally = true, isModifier = false)
+ private List testConditions = null;
+
+ public List getConditions() {
+ return this.testConditions;
+ }
+
+ public void setCondition(List ref) {
+ this.testConditions = ref;
+ }
+ }
+
+ private Matcher super String> stringContainsInOrder(java.lang.String... substrings) {
+ return Matchers.stringContainsInOrder(Arrays.asList(substrings));
+ }
+
+ private Matcher super String> stringContainsInOrder(List substrings) {
+ return Matchers.stringContainsInOrder(substrings);
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/FhirInstanceValidatorTest.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/FhirInstanceValidatorTest.java
new file mode 100644
index 00000000000..9adf2c836d9
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/FhirInstanceValidatorTest.java
@@ -0,0 +1,136 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import java.io.IOException;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.validation.FhirValidator;
+import ca.uhn.fhir.validation.ValidationResult;
+import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
+import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
+import org.hl7.fhir.instance.model.DateType;
+import org.hl7.fhir.instance.model.Observation;
+import org.hl7.fhir.instance.model.QuestionnaireResponse;
+import org.hl7.fhir.instance.model.StringType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_VALIDATION_HL7ORG_DSTU2;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class FhirInstanceValidatorTest {
+ private FhirInstanceValidator ourValidator = new FhirInstanceValidator(new DefaultProfileValidationSupport());
+ private FhirContext ourCtxHl7OrgDstu2 = FhirContext.forDstu2Hl7Org();
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidatorTest.class);
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_VALIDATION_HL7ORG_DSTU2.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ @Test
+ public void testQuestionnaireResponse() {
+ QuestionnaireResponse qr = new QuestionnaireResponse();
+ qr.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.COMPLETED);
+ qr.getGroup().addGroup().addQuestion().setLinkId("foo");
+ qr.getGroup().addQuestion().setLinkId("bar");
+
+ FhirValidator val = ourCtxHl7OrgDstu2.newValidator();
+
+ val.registerValidatorModule(ourValidator);
+
+ ValidationResult result = val.validateWithResult(qr);
+
+ String encoded = ourCtxHl7OrgDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
+ ourLog.info(encoded);
+
+ assertTrue(result.isSuccessful());
+ }
+
+ @Test
+ public void testObservation() {
+ Observation o = new Observation();
+ o.addIdentifier().setSystem("http://acme.org").setValue("1234");
+ o.setStatus(Observation.ObservationStatus.FINAL);
+ o.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
+ o.getEncounter().setReference("Encounter/1234");
+
+ FhirValidator val = ourCtxHl7OrgDstu2.newValidator();
+
+ val.registerValidatorModule(ourValidator);
+
+ ValidationResult result = val.validateWithResult(o);
+
+ String encoded = ourCtxHl7OrgDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
+ ourLog.info(encoded);
+
+ assertTrue(result.isSuccessful());
+ }
+
+ @Test
+ public void testParametersWithTwoParameters() {
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addName().addGiven("James");
+ patient.setBirthDateElement(new DateType("2011-02-02"));
+
+ org.hl7.fhir.instance.model.Parameters input = new org.hl7.fhir.instance.model.Parameters();
+ input.addParameter().setName("mode").setValue(new StringType("create"));
+ input.addParameter().setName("resource").setResource(patient);
+
+ FhirValidator val = ourCtxHl7OrgDstu2.newValidator();
+
+ val.registerValidatorModule(ourValidator);
+
+ ValidationResult result = val.validateWithResult(input);
+
+ String encoded = ourCtxHl7OrgDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
+ ourLog.info(encoded);
+
+ assertTrue(result.isSuccessful());
+ assertThat(encoded, not(containsString("A parameter must have a value or a resource, but not both")));
+ }
+
+ @Test
+ public void testParametersHl7OrgDstu2() {
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addName().addGiven("James");
+ patient.setBirthDateElement(new DateType("2011-02-02"));
+
+ org.hl7.fhir.instance.model.Parameters input = new org.hl7.fhir.instance.model.Parameters();
+ input.addParameter().setName("resource").setResource(patient);
+
+ FhirValidator val = ourCtxHl7OrgDstu2.newValidator();
+
+ val.registerValidatorModule(ourValidator);
+
+ ValidationResult result = val.validateWithResult(input);
+
+ ourLog.info(ourCtxHl7OrgDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
+ assertTrue(result.isSuccessful());
+ }
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/JsonParserHl7OrgDstu2Test.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/JsonParserHl7OrgDstu2Test.java
new file mode 100644
index 00000000000..28e98cd4656
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/JsonParserHl7OrgDstu2Test.java
@@ -0,0 +1,1320 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.List;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.narrative.INarrativeGenerator;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.rest.api.Constants;
+import org.apache.commons.io.IOUtils;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.IsNot;
+import org.hamcrest.core.StringContains;
+import org.hamcrest.text.StringContainsInOrder;
+import org.hl7.fhir.instance.model.Address;
+import org.hl7.fhir.instance.model.Address.AddressUse;
+import org.hl7.fhir.instance.model.Address.AddressUseEnumFactory;
+import org.hl7.fhir.instance.model.Binary;
+import org.hl7.fhir.instance.model.Bundle;
+import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.instance.model.CodeableConcept;
+import org.hl7.fhir.instance.model.Conformance;
+import org.hl7.fhir.instance.model.Conformance.UnknownContentCode;
+import org.hl7.fhir.instance.model.DateTimeType;
+import org.hl7.fhir.instance.model.DateType;
+import org.hl7.fhir.instance.model.DecimalType;
+import org.hl7.fhir.instance.model.DiagnosticReport;
+import org.hl7.fhir.instance.model.EnumFactory;
+import org.hl7.fhir.instance.model.Enumeration;
+import org.hl7.fhir.instance.model.Extension;
+import org.hl7.fhir.instance.model.HumanName;
+import org.hl7.fhir.instance.model.Identifier.IdentifierUse;
+import org.hl7.fhir.instance.model.InstantType;
+import org.hl7.fhir.instance.model.List_;
+import org.hl7.fhir.instance.model.Narrative.NarrativeStatus;
+import org.hl7.fhir.instance.model.Observation;
+import org.hl7.fhir.instance.model.Organization;
+import org.hl7.fhir.instance.model.Patient;
+import org.hl7.fhir.instance.model.Patient.ContactComponent;
+import org.hl7.fhir.instance.model.PrimitiveType;
+import org.hl7.fhir.instance.model.Reference;
+import org.hl7.fhir.instance.model.Specimen;
+import org.hl7.fhir.instance.model.StringType;
+import org.hl7.fhir.instance.model.ValueSet;
+import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
+import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.instance.model.api.INarrative;
+import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.hl7.fhir.utilities.xhtml.XhtmlNode;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.xml.sax.SAXException;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_HL7ORG_DSTU2;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class JsonParserHl7OrgDstu2Test {
+
+ private FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserHl7OrgDstu2Test.class);
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_HL7ORG_DSTU2.option(),
+ mavenBundle().groupId("org.xmlunit").artifactId("xmlunit-core").versionAsInProject(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlEnabled() {
+ try {
+ String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
+ Bundle bundle = (Bundle) ourCtx.newJsonParser().parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertEquals("http://lalaland.org", o1Id.getBaseUrl());
+ assertEquals("patient", o1Id.getResourceType());
+ assertEquals("pat1", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = FhirContext.forDstu2Hl7Org();
+ }
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnFhirContext() {
+ try {
+ String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
+ ourCtx.getParserOptions().setOverrideResourceIdWithBundleEntryFullUrl(false);
+ Bundle bundle = (Bundle) ourCtx.newJsonParser().parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = FhirContext.forDstu2Hl7Org();
+ }
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnParser() {
+ try {
+ String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
+ Bundle bundle = (Bundle) ourCtx.newJsonParser().setOverrideResourceIdWithBundleEntryFullUrl(false).parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = FhirContext.forDstu2Hl7Org();
+ }
+ }
+
+ @Test
+ public void testEncodeUndeclaredExtensionWithEnumerationContent() {
+ IParser parser = ourCtx.newJsonParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUse.HOME);
+ EnumFactory fact = new AddressUseEnumFactory();
+ PrimitiveType enumeration = new Enumeration(fact).setValue(AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue(enumeration);
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueCode\":\"home\"}]"));
+
+ MyPatientWithOneDeclaredEnumerationExtension actual = parser.parseResource(MyPatientWithOneDeclaredEnumerationExtension.class, val);
+ assertEquals(AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Enumeration ref = actual.getFoo();
+ assertEquals("home", ref.getValue().toCode());
+
+ }
+
+ @Test
+ public void testEncodeNarrativeSuppressed() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().addFamily("FAMILY");
+ patient.getMaritalStatus().addCoding().setCode("D");
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSuppressNarratives(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Patient"));
+ assertThat(encoded, stringContainsInOrder(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE));
+ assertThat(encoded, not(containsString("text")));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, containsString("maritalStatus"));
+ }
+
+ @Test
+ public void testEncodeAndParseExtensions() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ Extension ext = new Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
+ patient.getExtension().add(ext);
+
+ Extension parent = new Extension().setUrl("http://example.com#parent");
+ patient.getExtension().add(parent);
+ Extension child1 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.getExtension().add(child1);
+ Extension child2 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value2"));
+ parent.getExtension().add(child2);
+
+ Extension modExt = new Extension();
+ modExt.setUrl("http://example.com/extensions#modext");
+ modExt.setValue(new DateType("1995-01-02"));
+ patient.getModifierExtension().add(modExt);
+
+ HumanName name = patient.addName();
+ name.addFamily("Blah");
+ StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given"));
+ given.getExtension().add(ext2);
+
+ StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent");
+ given2.getExtension().add(given2ext);
+ given2ext.addExtension().setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD"));
+
+ String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newJsonParser().encodeResourceToString(patient);
+ assertThat(enc,
+ stringContainsInOrder("{\"resourceType\":\"Patient\",", "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}",
+ "{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}"));
+ assertThat(enc, stringContainsInOrder("\"modifierExtension\":[" + "{" + "\"url\":\"http://example.com/extensions#modext\"," + "\"valueDate\":\"1995-01-02\"" + "}" + "],"));
+ assertThat(enc, containsString("\"_given\":[" + "{" + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext\"," + "\"valueString\":\"given\"" + "}" + "]" + "}," + "{"
+ + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext_parent\"," + "\"extension\":[" + "{"
+ + "\"url\":\"http://examples.com#givenext_child\"," + "\"valueString\":\"CHILD\"" + "}" + "]" + "}" + "]" + "}"));
+
+ /*
+ * Now parse this back
+ */
+
+ Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, enc);
+ ext = parsed.getExtension().get(0);
+ assertEquals("http://example.com/extensions#someext", ext.getUrl());
+ assertEquals("2011-01-02T11:13:15", ((DateTimeType) ext.getValue()).getValueAsString());
+
+ parent = patient.getExtension().get(1);
+ assertEquals("http://example.com#parent", parent.getUrl());
+ assertNull(parent.getValue());
+ child1 = parent.getExtension().get(0);
+ assertEquals("http://example.com#child", child1.getUrl());
+ assertEquals("value1", ((StringType) child1.getValue()).getValueAsString());
+ child2 = parent.getExtension().get(1);
+ assertEquals("http://example.com#child", child2.getUrl());
+ assertEquals("value2", ((StringType) child2.getValue()).getValueAsString());
+
+ modExt = parsed.getModifierExtension().get(0);
+ assertEquals("http://example.com/extensions#modext", modExt.getUrl());
+ assertEquals("1995-01-02", ((DateType) modExt.getValue()).getValueAsString());
+
+ name = parsed.getName().get(0);
+
+ ext2 = name.getGiven().get(0).getExtension().get(0);
+ assertEquals("http://examples.com#givenext", ext2.getUrl());
+ assertEquals("given", ((StringType) ext2.getValue()).getValueAsString());
+
+ given2ext = name.getGiven().get(1).getExtension().get(0);
+ assertEquals("http://examples.com#givenext_parent", given2ext.getUrl());
+ assertNull(given2ext.getValue());
+ Extension given2ext2 = given2ext.getExtension().get(0);
+ assertEquals("http://examples.com#givenext_child", given2ext2.getUrl());
+ assertEquals("CHILD", ((StringType) given2ext2.getValue()).getValue());
+
+ }
+
+ @Test
+ public void testEncodeBinaryResource() {
+
+ Binary patient = new Binary();
+ patient.setContentType("foo");
+ patient.setContent(new byte[] { 1, 2, 3, 4 });
+
+ String val = ourCtx.newJsonParser().encodeResourceToString(patient);
+ assertEquals("{\"resourceType\":\"Binary\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}", val);
+
+ }
+
+ @Test
+ public void testEncodeBundle() throws InterruptedException {
+ Bundle b = new Bundle();
+
+ InstantType pub = InstantType.now();
+ b.getMeta().setLastUpdatedElement(pub);
+ Thread.sleep(2);
+
+ Patient p1 = new Patient();
+ p1.addName().addFamily("Family1");
+ p1.setId("1");
+ BundleEntryComponent entry = b.addEntry();
+ entry.setResource(p1);
+
+ Patient p2 = new Patient();
+ p2.setId("Patient/2");
+ p2.addName().addFamily("Family2");
+ entry = b.addEntry();
+ entry.setResource(p2);
+
+ BundleEntryComponent deletedEntry = b.addEntry();
+ Patient dp = new Patient();
+ deletedEntry.setResource(dp);
+
+ dp.setId(("3"));
+ InstantType nowDt = InstantType.withCurrentTime();
+ dp.getMeta().setLastUpdatedElement(nowDt);
+
+ String bundleString = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(bundleString);
+
+ // List strings = new ArrayList();
+ // strings.addAll(Arrays.asList("\"published\":\"" + pub.getValueAsString() + "\""));
+ // strings.addAll(Arrays.asList("\"id\":\"1\""));
+ // strings.addAll(Arrays.asList("\"id\":\"2\"", "\"rel\":\"alternate\"", "\"href\":\"http://foo/bar\""));
+ // strings.addAll(Arrays.asList("\"deleted\":\"" + nowDt.getValueAsString() + "\"", "\"id\":\"Patient/3\""));
+
+ //@formatter:off
+ String[] strings = new String[] {
+ "\"resourceType\": \"Bundle\",",
+ "\"lastUpdated\": \"" + pub.getValueAsString() + "\"",
+ "\"entry\": [",
+ "\"resource\": {",
+ "\"id\": \"1\"",
+ "\"resource\": {",
+ "\"id\": \"2\"",
+ "\"resource\": {",
+ "\"id\": \"3\"",
+ "\"meta\": {",
+ "\"lastUpdated\": \"" + nowDt.getValueAsString() + "\""
+ };
+ //@formatter:off
+ assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(Arrays.asList(strings)));
+
+ b.getEntry().remove(2);
+ bundleString = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
+ assertThat(bundleString, not(containsString("deleted")));
+
+ }
+
+
+ @Test
+ public void testEncodeBundleCategory() {
+
+ Bundle b = new Bundle();
+ BundleEntryComponent e = b.addEntry();
+
+ Patient pt = new Patient();
+ pt.addIdentifier().setSystem("idsystem");
+ e.setResource(pt);
+
+ b.getMeta().addTag().setSystem("scheme").setCode("term").setDisplay("label");
+
+ String val = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(b);
+ ourLog.info(val);
+
+ assertThat(val, StringContains.containsString("\"tag\":[{\"system\":\"scheme\",\"code\":\"term\",\"display\":\"label\"}]"));
+ b = ourCtx.newJsonParser().parseResource(Bundle.class, val);
+ assertEquals(1, b.getMeta().getTag().size());
+ assertEquals("scheme", b.getMeta().getTag().get(0).getSystem());
+ assertEquals("term", b.getMeta().getTag().get(0).getCode());
+ assertEquals("label", b.getMeta().getTag().get(0).getDisplay());
+
+ assertNotNull(b.getEntry().get(0).getResource());
+ Patient p = (Patient) b.getEntry().get(0).getResource();
+ assertEquals("idsystem", p.getIdentifier().get(0).getSystem());
+
+ }
+
+
+ @Test
+ public void testEncodeBundleEntryCategory() {
+
+ Bundle b = new Bundle();
+ BundleEntryComponent e = b.addEntry();
+ e.setResource(new Patient());
+ e.getResource().getMeta().addTag().setSystem("scheme").setCode( "term").setDisplay( "label");
+
+ String val = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(b);
+ ourLog.info(val);
+
+ assertThat(val, StringContains.containsString("{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"meta\":{\"tag\":[{\"system\":\"scheme\",\"code\":\"term\",\"display\":\"label\"}]}}}]}"));
+
+ b = ourCtx.newJsonParser().parseResource(Bundle.class, val);
+ assertEquals(1, b.getEntry().size());
+ assertEquals(1, b.getEntry().get(0).getResource().getMeta().getTag().size());
+ assertEquals("scheme", b.getEntry().get(0).getResource().getMeta().getTag().get(0).getSystem());
+ assertEquals("term", b.getEntry().get(0).getResource().getMeta().getTag().get(0).getCode());
+ assertEquals("label", b.getEntry().get(0).getResource().getMeta().getTag().get(0).getDisplay());
+
+ }
+
+
+ @Test
+ public void testEncodeContained() {
+ IParser jsonParser = ourCtx.newJsonParser().setPrettyPrint(true);
+
+ // Create an organization, note that the organization does not have an ID
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+
+ // Put the organization as a reference in the patient resource
+ patient.getManagingOrganization().setResource(org);
+
+ String encoded = jsonParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\"")));
+
+ // Create a bundle with just the patient resource
+ Bundle b = new Bundle();
+ b.addEntry().setResource(patient);
+
+ // Encode the bundle
+ encoded = jsonParser.encodeResourceToString(b);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\"")));
+
+ // Re-parse the bundle
+ patient = (Patient) jsonParser.parseResource(jsonParser.encodeResourceToString(patient));
+ assertEquals("#1", patient.getManagingOrganization().getReference());
+
+ assertNotNull(patient.getManagingOrganization().getResource());
+ org = (Organization) patient.getManagingOrganization().getResource();
+ assertEquals("#1", org.getIdElement().getValue());
+ assertEquals("Contained Test Organization", org.getName());
+
+ // And re-encode a second time
+ encoded = jsonParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\"")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("\"contained\":", "[", "\"contained\":"))));
+
+ // And re-encode once more, with the references cleared
+ patient.getContained().clear();
+ patient.getManagingOrganization().setReference(null);
+ encoded = jsonParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\"")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("\"contained\":", "[", "\"contained\":"))));
+
+ // And re-encode once more, with the references cleared and a manually set local ID
+ patient.getContained().clear();
+ patient.getManagingOrganization().setReference(null);
+ patient.getManagingOrganization().getResource().setId(("#333"));
+ encoded = jsonParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\": [", "\"id\": \"333\"", "\"identifier\"", "\"reference\": \"#333\"")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("\"contained\":", "[", "\"contained\":"))));
+
+ }
+
+ @Test
+ public void testEncodeContained__() {
+ // Create an organization
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue( "253345");
+ patient.getManagingOrganization().setResource(org);
+
+ // Create a bundle with just the patient resource
+ Bundle b = new Bundle();
+ b.addEntry().setResource(patient);
+
+ // Encode the buntdle
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\"", "resourceType\": \"Organization", "id\": \"1\"")));
+ assertThat(encoded, containsString("reference\": \"#1\""));
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("\"contained\"", "resourceType\": \"Organization", "id\": \"1\"")));
+ assertThat(encoded, containsString("reference\": \"#1\""));
+ }
+
+ @Test
+ public void testEncodeContainedResourcesMore() throws Exception {
+
+ DiagnosticReport rpt = new DiagnosticReport();
+ Specimen spm = new Specimen();
+ rpt.getText().setDivAsString("AAA");
+ rpt.addSpecimen().setResource(spm);
+
+ IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
+ String str = p.encodeResourceToString(rpt);
+
+ ourLog.info(str);
+ assertThat(str, StringContains.containsString("AAA"));
+ String substring = "\"reference\": \"#";
+ assertThat(str, StringContains.containsString(substring));
+
+ int idx = str.indexOf(substring) + substring.length();
+ int idx2 = str.indexOf('"', idx + 1);
+ String id = str.substring(idx, idx2);
+ assertThat(str, StringContains.containsString("\"id\": \"" + id + "\""));
+ assertThat(str, IsNot.not(StringContains.containsString("")));
+
+ }
+
+ @Test
+ public void testEncodeContainedWithNarrativeIsSuppresed() throws Exception {
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+
+ // Create an organization, note that the organization does not have an ID
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+ org.getText().setDivAsString("FOOBAR");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue( "253345");
+ patient.getText().setDivAsString("BARFOO");
+ patient.getManagingOrganization().setResource(org);
+
+ String encoded = parser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, not(containsString("FOOBAR")));
+ assertThat(encoded, (containsString("BARFOO")));
+
+ }
+
+
+
+ @Test
+ public void testEncodeDeclaredExtensionWithAddressContent() {
+ IParser parser = ourCtx.newJsonParser();
+
+ MyPatientWithOneDeclaredAddressExtension patient = new MyPatientWithOneDeclaredAddressExtension();
+ patient.addAddress().setUse(AddressUse.HOME);
+ patient.setFoo(new Address().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Address ref = actual.getFoo();
+ assertEquals("line1", ref.getLine().get(0).getValue());
+
+ }
+
+ @Test
+ public void testEncodeDeclaredExtensionWithResourceContent() {
+ IParser parser = ourCtx.newJsonParser();
+
+ MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
+ patient.addAddress().setUse(AddressUse.HOME);
+ patient.setFoo(new Reference("Organization/123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueReference\":{\"reference\":\"Organization/123\"}}]"));
+
+ MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
+ assertEquals(AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Reference ref = actual.getFoo();
+ assertEquals("Organization/123", ref.getReference());
+
+ }
+
+ @Test
+ public void testEncodeExt() throws Exception {
+
+ ValueSet valueSet = new ValueSet();
+ valueSet.setId("123456");
+
+ ValueSetCodeSystemComponent define = valueSet.getCodeSystem();
+ ConceptDefinitionComponent code = define.addConcept();
+ code.setCode("someCode");
+ code.setDisplay("someDisplay");
+ code.addExtension().setUrl("urn:alt").setValue( new StringType("alt name"));
+
+
+ String encoded = ourCtx.newJsonParser().encodeResourceToString(valueSet);
+ ourLog.info(encoded);
+
+ assertThat(encoded, (containsString("123456")));
+ assertEquals(
+ "{\"resourceType\":\"ValueSet\",\"id\":\"123456\",\"codeSystem\":{\"concept\":[{\"extension\":[{\"url\":\"urn:alt\",\"valueString\":\"alt name\"}],\"code\":\"someCode\",\"display\":\"someDisplay\"}]}}",
+ encoded);
+
+ }
+
+ @Test
+ public void testEncodeExtensionInCompositeElement() {
+
+ Conformance c = new Conformance();
+ c.addRest().getSecurity().addExtension().setUrl("http://foo").setValue(new StringType("AAA"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c);
+ ourLog.info(encoded);
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
+ ourLog.info(encoded);
+ assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"rest\":[{\"security\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}]}");
+
+ }
+
+
+
+ @Test
+ public void testEncodeExtensionInPrimitiveElement() {
+
+ Conformance c = new Conformance();
+ c.getAcceptUnknownElement().addExtension().setUrl( "http://foo").setValue( new StringType("AAA"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c);
+ ourLog.info(encoded);
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
+ ourLog.info(encoded);
+ assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"_acceptUnknown\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}");
+
+ // Now with a value
+ ourLog.info("---------------");
+
+ c = new Conformance();
+ c.getAcceptUnknownElement().setValue(UnknownContentCode.EXTENSIONS);
+ c.getAcceptUnknownElement().addExtension().setUrl("http://foo").setValue( new StringType("AAA"));
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c);
+ ourLog.info(encoded);
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
+ ourLog.info(encoded);
+ assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"acceptUnknown\":\"extensions\",\"_acceptUnknown\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}");
+
+ }
+
+ @Test
+ public void testEncodeExtensionInResourceElement() {
+
+ Conformance c = new Conformance();
+ // c.addRest().getSecurity().addUndeclaredExtension(false, "http://foo", new StringType("AAA"));
+ c.addExtension().setUrl("http://foo").setValue( new StringType("AAA"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c);
+ ourLog.info(encoded);
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
+ ourLog.info(encoded);
+ assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}");
+
+ }
+
+ @Test
+ public void testEncodeExtensionOnEmptyElement() throws Exception {
+
+ ValueSet valueSet = new ValueSet();
+ valueSet.addUseContext().addExtension().setUrl("http://foo").setValue( new StringType("AAA"));
+
+ String encoded = ourCtx.newJsonParser().encodeResourceToString(valueSet);
+ assertThat(encoded, containsString("\"useContext\":[{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}"));
+
+ }
+
+
+ @Test
+ public void testEncodeExtensionWithResourceContent() {
+ IParser parser = ourCtx.newJsonParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue( new Reference("Organization/123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueReference\":{\"reference\":\"Organization/123\"}}]"));
+
+ Patient actual = parser.parseResource(Patient.class, val);
+ assertEquals(AddressUse.HOME, patient.getAddress().get(0).getUse());
+ List ext = actual.getExtension();
+ assertEquals(1, ext.size());
+ Reference ref = (Reference) ext.get(0).getValue();
+ assertEquals("Organization/123", ref.getReference());
+
+ }
+
+ @Test
+ public void testEncodeIds() {
+ Patient pt = new Patient();
+ pt.addIdentifier().setSystem("sys").setValue( "val");
+
+ List_ list = new List_();
+ list.setId("listId");
+ list.addEntry().setItem(new Reference(pt)).setDeleted(true);
+
+ String enc = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(list);
+ ourLog.info(enc);
+
+ assertThat(enc, containsString("\"id\": \"1\""));
+
+ List_ parsed = ourCtx.newJsonParser().parseResource(List_.class,enc);
+ assertEquals(Patient.class, parsed.getEntry().get(0).getItem().getResource().getClass());
+ }
+
+ @Test
+ public void testEncodeInvalidChildGoodException() {
+ Observation obs = new Observation();
+ obs.setValue(new DecimalType(112.22));
+
+ IParser p = ourCtx.newJsonParser();
+
+ try {
+ p.encodeResourceToString(obs);
+ } catch (DataFormatException e) {
+ assertThat(e.getMessage(), StringContains.containsString("DecimalType"));
+ }
+ }
+
+ @Test
+ public void testEncodeNarrativeBlockInBundle() throws Exception {
+ Patient p = new Patient();
+ p.addIdentifier().setSystem("foo").setValue("bar");
+ p.getText().setStatus(NarrativeStatus.GENERATED);
+ p.getText().setDivAsString("AAA");
+
+ Bundle b = new Bundle();
+ b.setTotal(123);
+ b.addEntry().setResource(p);
+
+ String str = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(str);
+ assertThat(str, StringContains.containsString("AAA"));
+
+ p.getText().setDivAsString("hello ");
+ str = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(str);
+ // Backslashes need to be escaped because they are in a JSON value
+ assertThat(str, containsString(">hello<"));
+
+ }
+
+ @Test
+ public void testEncodeNonContained() {
+ Organization org = new Organization();
+ org.setId("Organization/65546");
+ org.getNameElement().setValue("Contained Test Organization");
+
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+ patient.getManagingOrganization().setResource(org);
+
+ Bundle b = new Bundle();
+ b.addEntry().setResource(patient);
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(encoded);
+ assertThat(encoded, not(containsString("contained")));
+ assertThat(encoded, containsString("\"reference\": \"Organization/65546\""));
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, not(containsString("contained")));
+ assertThat(encoded, containsString("\"reference\": \"Organization/65546\""));
+ }
+
+
+
+
+ @Test
+ public void testEncodeResourceRef() throws DataFormatException {
+
+ Patient patient = new Patient();
+ patient.setManagingOrganization(new Reference());
+
+ IParser p = ourCtx.newJsonParser();
+ String str = p.encodeResourceToString(patient);
+ assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
+
+ patient.setManagingOrganization(new Reference("Organization/123"));
+ str = p.encodeResourceToString(patient);
+ assertThat(str, StringContains.containsString("\"managingOrganization\":{\"reference\":\"Organization/123\"}"));
+
+ Organization org = new Organization();
+ org.addIdentifier().setSystem("foo").setValue("bar");
+ patient.setManagingOrganization(new Reference(org));
+ str = p.encodeResourceToString(patient);
+ assertThat(str, StringContains.containsString("\"contained\":[{\"resourceType\":\"Organization\""));
+
+ }
+
+ @Test
+ public void testEncodeSummary() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().addFamily("FAMILY");
+ patient.setMaritalStatus(new CodeableConcept().setText("D"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Patient"));
+ assertThat(encoded, stringContainsInOrder("\"tag\"",
+ "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE+"\","));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeSummary2() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().addFamily("FAMILY");
+ patient.setMaritalStatus(new CodeableConcept().setText("D"));
+
+ patient.getMeta().addTag().setSystem("foo").setCode("bar");
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Patient"));
+ assertThat(encoded, stringContainsInOrder("\"tag\"",
+ "\"system\": \"foo\",", "\"code\": \"bar\"",
+ "\"system\": \"" + Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + Constants.TAG_SUBSETTED_CODE+"\","));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeUndeclaredExtensionWithAddressContent() {
+ IParser parser = ourCtx.newJsonParser();
+
+ Patient patient = new Patient();
+ patient.addAddress().setUse(AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue(new Address().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Address ref = actual.getFoo();
+ assertEquals("line1", ref.getLine().get(0).getValue());
+
+ }
+
+ @Test
+ public void testEncodingNullExtension() {
+ Patient p = new Patient();
+ Extension extension = new Extension().setUrl("http://foo#bar");
+ p.getExtension().add(extension);
+ String str = ourCtx.newJsonParser().encodeResourceToString(p);
+
+ assertEquals("{\"resourceType\":\"Patient\"}", str);
+
+ extension.setValue(new StringType());
+
+ str = ourCtx.newJsonParser().encodeResourceToString(p);
+ assertEquals("{\"resourceType\":\"Patient\"}", str);
+
+ extension.setValue(new StringType(""));
+
+ str = ourCtx.newJsonParser().encodeResourceToString(p);
+ assertEquals("{\"resourceType\":\"Patient\"}", str);
+
+ }
+
+ @Test
+ public void testExtensionOnComposite() throws Exception {
+
+ Patient patient = new Patient();
+
+ HumanName name = patient.addName();
+ name.addFamily("Shmoe");
+ HumanName given = name.addGiven("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue( new StringType("Hello"));
+ given.getExtension().add(ext2);
+ String enc = ourCtx.newJsonParser().encodeResourceToString(patient);
+ ourLog.info(enc);
+ assertEquals("{\"resourceType\":\"Patient\",\"name\":[{\"extension\":[{\"url\":\"http://examples.com#givenext\",\"valueString\":\"Hello\"}],\"family\":[\"Shmoe\"],\"given\":[\"Joe\"]}]}", enc);
+
+ IParser newJsonParser = ourCtx.newJsonParser();
+ StringReader reader = new StringReader(enc);
+ Patient parsed = newJsonParser.parseResource(Patient.class, reader);
+
+ ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parsed));
+
+ assertEquals(1, parsed.getName().get(0).getExtension().size());
+ Extension ext = parsed.getName().get(0).getExtension().get(0);
+ assertEquals("Hello", ((IPrimitiveType>)ext.getValue()).getValue());
+
+ }
+
+ @Test
+ public void testExtensionOnPrimitive() throws Exception {
+
+ Patient patient = new Patient();
+
+ HumanName name = patient.addName();
+ StringType family = name.addFamilyElement();
+ family.setValue("Shmoe");
+
+ family.addExtension().setUrl("http://examples.com#givenext").setValue( new StringType("Hello"));
+ String enc = ourCtx.newJsonParser().encodeResourceToString(patient);
+ ourLog.info(enc);
+ //@formatter:off
+ assertThat(enc, containsString(("{\n" +
+ " \"resourceType\":\"Patient\",\n" +
+ " \"name\":[\n" +
+ " {\n" +
+ " \"family\":[\n" +
+ " \"Shmoe\"\n" +
+ " ],\n" +
+ " \"_family\":[\n" +
+ " {\n" +
+ " \"extension\":[\n" +
+ " {\n" +
+ " \"url\":\"http://examples.com#givenext\",\n" +
+ " \"valueString\":\"Hello\"\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ "}").replace("\n", "").replaceAll(" +", "")));
+ //@formatter:on
+
+ Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, new StringReader(enc));
+ assertEquals(1, parsed.getName().get(0).getFamily().get(0).getExtension().size());
+ Extension ext = parsed.getName().get(0).getFamily().get(0).getExtension().get(0);
+ assertEquals("Hello", ((IPrimitiveType>) ext.getValue()).getValue());
+
+ }
+
+ @Test
+ public void testMoreExtensions() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ Extension ext = new Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
+
+ // Add the extension to the resource
+ patient.getExtension().add(ext);
+ // END SNIPPET: resourceExtension
+
+ // START SNIPPET: resourceStringExtension
+ HumanName name = patient.addName();
+ name.addFamily("Shmoe");
+ StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given"));
+ given.getExtension().add(ext2);
+
+ StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent");
+ given2.getExtension().add(given2ext);
+ given2ext.addExtension().setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD"));
+ // END SNIPPET: resourceStringExtension
+
+ // START SNIPPET: subExtension
+ Extension parent = new Extension().setUrl("http://example.com#parent");
+ patient.getExtension().add(parent);
+
+ Extension child1 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.getExtension().add(child1);
+
+ Extension child2 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.getExtension().add(child2);
+ // END SNIPPET: subExtension
+
+ String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newJsonParser().encodeResourceToString(patient);
+ //@formatter:off
+ assertThat(enc, containsString(("{" +
+ "\"resourceType\":\"Patient\"," +
+ " \"extension\":[" +
+ " {" +
+ " \"url\":\"http://example.com/extensions#someext\"," +
+ " \"valueDateTime\":\"2011-01-02T11:13:15\"" +
+ " }," +
+ " {" +
+ " \"url\":\"http://example.com#parent\"," +
+ " \"extension\":[" +
+ " {" +
+ " \"url\":\"http://example.com#child\"," +
+ " \"valueString\":\"value1\"" +
+ " }," +
+ " {" +
+ " \"url\":\"http://example.com#child\"," +
+ " \"valueString\":\"value1\"" +
+ " }" +
+ " ]" +
+ " }" +
+ " ]").replace(" ", "")));
+ //@formatter:on
+
+ //@formatter:off
+ assertThat(enc, containsString((
+ " \"given\":[" +
+ " \"Joe\"," +
+ " \"Shmoe\"" +
+ " ]," +
+ " \"_given\":[" +
+ " {" +
+ " \"extension\":[" +
+ " {" +
+ " \"url\":\"http://examples.com#givenext\"," +
+ " \"valueString\":\"given\"" +
+ " }" +
+ " ]" +
+ " }," +
+ " {" +
+ " \"extension\":[" +
+ " {" +
+ " \"url\":\"http://examples.com#givenext_parent\"," +
+ " \"extension\":[" +
+ " {" +
+ " \"url\":\"http://examples.com#givenext_child\"," +
+ " \"valueString\":\"CHILD\"" +
+ " }" +
+ " ]" +
+ " }" +
+ " ]" +
+ " }" +
+ "").replace(" ", "")));
+ //@formatter:on
+ }
+
+ @Test
+ public void testNestedContainedResources() {
+
+ Observation A = new Observation();
+ A.getCode().setText("A");
+
+ Observation B = new Observation();
+ B.getCode().setText("B");
+ A.addRelated().setTarget(new Reference(B));
+
+ Observation C = new Observation();
+ C.getCode().setText("C");
+ B.addRelated().setTarget(new Reference(C));
+
+ String str = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(A);
+ ourLog.info(str);
+
+ assertThat(str, stringContainsInOrder(Arrays.asList("\"text\": \"B\"", "\"text\": \"C\"", "\"text\": \"A\"")));
+
+ // Only one (outer) contained block
+ int idx0 = str.indexOf("\"contained\"");
+ int idx1 = str.indexOf("\"contained\"", idx0 + 1);
+
+ assertNotEquals(-1, idx0);
+ assertEquals(-1, idx1);
+
+ Observation obs = ourCtx.newJsonParser().parseResource(Observation.class, str);
+ assertEquals("A", obs.getCode().getTextElement().getValue());
+
+ Observation obsB = (Observation) obs.getRelated().get(0).getTarget().getResource();
+ assertEquals("B", obsB.getCode().getTextElement().getValue());
+
+ Observation obsC = (Observation) obsB.getRelated().get(0).getTarget().getResource();
+ assertEquals("C", obsC.getCode().getTextElement().getValue());
+
+ }
+
+ @Test
+ public void testParseBinaryResource() {
+
+ Binary val = ourCtx.newJsonParser().parseResource(Binary.class, "{\"resourceType\":\"Binary\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}");
+ assertEquals("foo", val.getContentType());
+ assertArrayEquals(new byte[] { 1, 2, 3, 4 }, val.getContent());
+
+ }
+
+ @Test
+ public void testParseEmptyNarrative() throws Exception {
+ //@formatter:off
+ String text = "{\n" +
+ " \"resourceType\" : \"Patient\",\n" +
+ " \"extension\" : [\n" +
+ " {\n" +
+ " \"url\" : \"http://clairol.org/colour\",\n" +
+ " \"valueCode\" : \"B\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"text\" : {\n" +
+ " \"div\" : \"\"\n" +
+ " }" +
+ "}";
+ //@formatter:on
+
+ Patient res = (Patient) ourCtx.newJsonParser().parseResource(text);
+ XhtmlNode div = res.getText().getDiv();
+ String value = div.getValueAsString();
+
+ assertNull(value);
+ List childNodes = div.getChildNodes();
+ assertTrue(childNodes == null || childNodes.isEmpty());
+ }
+
+ @Test
+ public void testParseSimpleBundle() {
+ String bundle = "{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"identifier\":[{\"system\":\"idsystem\"}]}}]}";
+ Bundle b = ourCtx.newJsonParser().parseResource(Bundle.class, bundle);
+
+ assertNotNull(b.getEntry().get(0).getResource());
+ Patient p = (Patient) b.getEntry().get(0).getResource();
+ assertEquals("idsystem", p.getIdentifier().get(0).getSystem());
+ }
+
+ @Test
+ public void testParseSingleQuotes() {
+ ourCtx.newJsonParser().parseResource(Bundle.class, "{ \"resourceType\": \"Bundle\" }");
+ ourCtx.newJsonParser().parseResource(Bundle.class, "{ 'resourceType': 'Bundle' }");
+ }
+
+ @Test
+ public void testSimpleParse() throws DataFormatException, IOException {
+
+ String msg = IOUtils.toString(JsonParserHl7OrgDstu2Test.class.getResourceAsStream("/example-patient-general-hl7orgdstu2.json"));
+ IParser p = ourCtx.newJsonParser();
+ // ourLog.info("Reading in message: {}", msg);
+ Patient res = p.parseResource(Patient.class, msg);
+
+ assertEquals(2, res.getExtension().size());
+ assertEquals(1, res.getModifierExtension().size());
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
+ ourLog.info(encoded);
+
+ }
+
+ @Test
+ public void testParsePrimitiveExtension() {
+ //@formatter:off
+ String input = "{\n" +
+ " \"resourceType\":\"Patient\",\n" +
+ " \"contact\":[\n" +
+ " {\n" +
+ " \"name\":{\n" +
+ " \"family\":[\n" +
+ " \"du\",\n" +
+ " \"Marché\"\n" +
+ " ],\n" +
+ " \"_family\":[\n" +
+ " {\n" +
+ " \"extension\":[\n" +
+ " {\n" +
+ " \"url\":\"http://hl7.org/fhir/Profile/iso-21090#qualifier\",\n" +
+ " \"valueCode\":\"VV\"\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " null\n" +
+ " ]\n" +
+ " }\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+ //@formatter:off
+
+ Patient p = ourCtx.newJsonParser().parseResource(Patient.class, input);
+ ContactComponent contact = p.getContact().get(0);
+ StringType family = contact.getName().getFamily().get(0);
+
+ assertEquals("du", family.getValueAsString());
+ assertEquals(1, family.getExtension().size());
+ }
+
+
+ @Test
+ public void testSimpleResourceEncodeWithCustomType() throws IOException, SAXException {
+
+ String jsonString = IOUtils.toString(JsonParserHl7OrgDstu2Test.class.getResourceAsStream("/example-patient-general-hl7orgdstu2.json"), Charset.forName("UTF-8"));
+ MyObservationWithExtensions obs = ourCtx.newJsonParser().parseResource(MyObservationWithExtensions.class, jsonString);
+
+ {
+ ContactComponent contact = obs.getContact().get(0);
+ StringType family = contact.getName().getFamily().get(0);
+ assertEquals("du", family.getValueAsString());
+ assertEquals(1, family.getExtension().size());
+ }
+
+ assertEquals(0, obs.getExtension().size());
+ assertEquals("aaaa", obs.getExtAtt().getContentType());
+ assertEquals("str1", obs.getMoreExt().getStr1().getValue());
+ assertEquals("2011-01-02", obs.getModExt().getValueAsString());
+
+ List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getExtension();
+ Extension undeclaredExtension = undeclaredExtensions.get(0);
+ assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl());
+
+ IParser xmlParser = ourCtx.newXmlParser();
+ String encoded = xmlParser.encodeResourceToString(obs);
+ encoded = encoded.replaceAll("", "").replace("\n", "").replace("\r", "").replaceAll(">\\s+<", "><");
+
+ String xmlString = IOUtils.toString(JsonParserHl7OrgDstu2Test.class.getResourceAsStream("/example-patient-general-hl7orgdstu2.xml"), Charset.forName("UTF-8"));
+ xmlString = xmlString.replaceAll("", "").replace("\n", "").replace("\r", "").replaceAll(">\\s+<", "><");
+
+ ourLog.info("Expected: " + xmlString);
+ ourLog.info("Actual : " + encoded);
+
+ String expected = (xmlString);
+ String actual = (encoded.trim());
+
+ XmlParserHl7OrgDstu2Test.compareXml(expected, actual);
+ }
+
+
+ @Test
+ public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
+ String refVal = "http://my.org/FooBar";
+
+ Patient fhirPat = new Patient();
+ fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
+
+ IParser parser = ourCtx.newJsonParser();
+
+ String output = parser.encodeResourceToString(fhirPat);
+ System.out.println("output: " + output);
+
+ // Deserialize then check that valueReference value is still correct
+ fhirPat = parser.parseResource(Patient.class, output);
+
+ List extlst = fhirPat.getExtension();
+ Assert.assertEquals(1, extlst.size());
+ Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class MyPatientWithOneDeclaredAddressExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(order = 0, name = "foo")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private Address myFoo;
+
+ public Address getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(Address theFoo) {
+ myFoo = theFoo;
+ }
+
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class MyPatientWithOneDeclaredExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(order = 0, name = "foo")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private Reference myFoo;
+
+ public Reference getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(Reference theFoo) {
+ myFoo = theFoo;
+ }
+
+ }
+
+ private Matcher super String> stringContainsInOrder(java.lang.String... substrings) {
+ return Matchers.stringContainsInOrder(Arrays.asList(substrings));
+ }
+
+ private Matcher super String> stringContainsInOrder(List substrings) {
+ return Matchers.stringContainsInOrder(substrings);
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyObservationWithExtensions.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyObservationWithExtensions.java
new file mode 100644
index 00000000000..f9b030253d9
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyObservationWithExtensions.java
@@ -0,0 +1,106 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import ca.uhn.fhir.model.api.annotation.Block;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.util.ElementUtil;
+import org.hl7.fhir.instance.model.Attachment;
+import org.hl7.fhir.instance.model.BackboneElement;
+import org.hl7.fhir.instance.model.DateType;
+import org.hl7.fhir.instance.model.Patient;
+import org.hl7.fhir.instance.model.StringType;
+import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
+
+
+@ResourceDef(name="Patient")
+public class MyObservationWithExtensions extends Patient {
+
+ @Extension(url = "urn:patientext:att", definedLocally = false, isModifier = false)
+ @Child(name = "extAtt", order = 0)
+ private Attachment myExtAtt;
+
+ @Extension(url = "urn:patientext:moreext", definedLocally = false, isModifier = false)
+ @Child(name = "moreExt", order = 1)
+ private MoreExt myMoreExt;
+
+ @Extension(url = "urn:modext", definedLocally = false, isModifier = true)
+ @Child(name = "modExt", order = 2)
+ private DateType myModExt;
+
+ public Attachment getExtAtt() {
+ return myExtAtt;
+ }
+
+ public MoreExt getMoreExt() {
+ return myMoreExt;
+ }
+
+ public void setMoreExt(MoreExt theMoreExt) {
+ myMoreExt = theMoreExt;
+ }
+
+ public DateType getModExt() {
+ return myModExt;
+ }
+
+ public void setModExt(DateType theModExt) {
+ myModExt = theModExt;
+ }
+
+ public void setExtAtt(Attachment theExtAtt) {
+ myExtAtt = theExtAtt;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && ElementUtil.isEmpty(myExtAtt, myModExt, myMoreExt);
+ }
+
+ /**
+ * Block class for child element: Observation.referenceRange (Provides guide for interpretation)
+ *
+ *
+ * Definition: Guidance on how to interpret the value by comparison to a normal or recommended range
+ *
+ */
+ @Block(name = "Observation.someExtensions")
+ public static class MoreExt extends BackboneElement implements IBaseBackboneElement {
+
+ @Extension(url = "urn:patientext:moreext:1", definedLocally = false, isModifier = false)
+ @Child(name = "str1", order = 0)
+ private StringType myStr1;
+
+ @Extension(url = "urn:patientext:moreext:2", definedLocally = false, isModifier = false)
+ @Child(name = "str2", order = 1)
+ private StringType myStr2;
+
+ public StringType getStr1() {
+ return myStr1;
+ }
+
+ public void setStr1(StringType theStr1) {
+ myStr1 = theStr1;
+ }
+
+ public StringType getStr2() {
+ return myStr2;
+ }
+
+ public void setStr2(StringType theStr2) {
+ myStr2 = theStr2;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return ElementUtil.isEmpty(myStr1, myStr2);
+ }
+
+ @Override
+ public BackboneElement copy() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyOrganizationDstu2.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyOrganizationDstu2.java
new file mode 100644
index 00000000000..d0d6820a724
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyOrganizationDstu2.java
@@ -0,0 +1,11 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.instance.model.Organization;
+
+@ResourceDef()
+public class MyOrganizationDstu2 extends Organization {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyPatientHl7Org.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyPatientHl7Org.java
new file mode 100644
index 00000000000..c76e2bbf8fb
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyPatientHl7Org.java
@@ -0,0 +1,64 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import java.util.ArrayList;
+import java.util.List;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.instance.model.DateTimeType;
+import org.hl7.fhir.instance.model.Patient;
+import org.hl7.fhir.instance.model.Reference;
+import org.hl7.fhir.instance.model.StringType;
+
+
+@ResourceDef()
+public class MyPatientHl7Org extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name="petName")
+ @Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
+ @Description(shortDefinition="The name of the patient's favourite pet")
+ private StringType myPetName;
+
+ @Child(name="importantDates", max=Child.MAX_UNLIMITED)
+ @Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
+ @Description(shortDefinition="Some dates of note for the patient")
+ private List myImportantDates;
+
+ @Child(name="managingOrganization", order=Child.REPLACE_PARENT, min=0, max=1, type={
+ MyOrganizationDstu2.class })
+ @Description(
+ shortDefinition="Organization that is the custodian of the patient record",
+ formalDefinition="Organization that is the custodian of the patient record"
+ )
+ private Reference myManagingOrganization;
+
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && myPetName.isEmpty();
+ }
+
+
+ public List getImportantDates() {
+ if (myImportantDates==null) {
+ myImportantDates = new ArrayList();
+ }
+ return myImportantDates;
+ }
+
+ public StringType getPetName() {
+ return myPetName;
+ }
+
+ public void setImportantDates(List theImportantDates) {
+ myImportantDates = theImportantDates;
+ }
+
+ public void setPetName(StringType thePetName) {
+ myPetName = thePetName;
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyPatientWithOneDeclaredEnumerationExtension.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyPatientWithOneDeclaredEnumerationExtension.java
new file mode 100644
index 00000000000..2f62b1b27fe
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/MyPatientWithOneDeclaredEnumerationExtension.java
@@ -0,0 +1,26 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.instance.model.Address.AddressUse;
+import org.hl7.fhir.instance.model.Enumeration;
+import org.hl7.fhir.instance.model.Patient;
+
+@ResourceDef(name = "Patient")
+public class MyPatientWithOneDeclaredEnumerationExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(order = 0, name = "foo")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private Enumeration myFoo;
+
+ public Enumeration getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(Enumeration theFoo) {
+ myFoo = theFoo;
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/ResourceWithExtensionsA.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/ResourceWithExtensionsA.java
new file mode 100644
index 00000000000..5f8241084cf
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/ResourceWithExtensionsA.java
@@ -0,0 +1,206 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import java.util.List;
+import ca.uhn.fhir.model.api.annotation.Block;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.instance.model.BackboneElement;
+import org.hl7.fhir.instance.model.CodeableConcept;
+import org.hl7.fhir.instance.model.DateType;
+import org.hl7.fhir.instance.model.DomainResource;
+import org.hl7.fhir.instance.model.Identifier;
+import org.hl7.fhir.instance.model.ResourceType;
+import org.hl7.fhir.instance.model.StringType;
+
+@ResourceDef(name = "ResourceWithExtensionsA", id="0001")
+public class ResourceWithExtensionsA extends DomainResource {
+
+ /*
+ * NB: several unit tests depend on the structure here
+ * so check the unit tests immediately after any changes
+ */
+
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "foo1", type = StringType.class, order = 0, min = 0, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://foo/#f1", definedLocally=true, isModifier=false)
+ private List myFoo1;
+
+ @Child(name = "foo2", type = StringType.class, order = 1, min = 0, max = 1)
+ @Extension(url = "http://foo/#f2", definedLocally=true, isModifier=true)
+ private StringType myFoo2;
+
+ @Child(name = "bar1", type = Bar1.class, order = 2, min = 1, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://bar/#b1", definedLocally=true, isModifier=false)
+ private List myBar1;
+
+ @Child(name = "bar2", type = Bar1.class, order = 3, min = 1, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://bar/#b2", definedLocally=true, isModifier=false)
+ private Bar1 myBar2;
+
+ @Child(name="baz", type = CodeableConcept.class, order = 4)
+ @Extension(url= "http://baz/#baz", definedLocally=true, isModifier=false)
+ @Description(shortDefinition = "Contains a codeable concept")
+ private CodeableConcept myBaz;
+
+ @Child(name = "identifier", type = Identifier.class, order = 0, min = 0, max = Child.MAX_UNLIMITED)
+ private List myIdentifier;
+
+ public List getBar1() {
+ return myBar1;
+ }
+
+ public Bar1 getBar2() {
+ return myBar2;
+ }
+
+ public List getFoo1() {
+ return myFoo1;
+ }
+
+ public StringType getFoo2() {
+ return myFoo2;
+ }
+
+ public CodeableConcept getBaz() { return myBaz; }
+
+ public List getIdentifier() {
+ return myIdentifier;
+ }
+
+ public void setBar1(List theBar1) {
+ myBar1 = theBar1;
+ }
+
+ public void setBar2(Bar1 theBar2) {
+ myBar2 = theBar2;
+ }
+
+ public void setFoo1(List theFoo1) {
+ myFoo1 = theFoo1;
+ }
+
+ public void setFoo2(StringType theFoo2) {
+ myFoo2 = theFoo2;
+ }
+
+ public void setBaz(CodeableConcept myBaz) { this.myBaz = myBaz; }
+
+ public void setIdentifier(List theValue) {
+ myIdentifier = theValue;
+ }
+
+ @Block(name = "Bar1")
+ public static class Bar1 extends BackboneElement {
+
+ public Bar1() {
+ super();
+ }
+
+ @Child(name = "bar11", type = DateType.class, order = 0, min = 0, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://bar/#b1/1", definedLocally=true, isModifier=false)
+ private List myBar11;
+
+ @Child(name = "bar12", type = DateType.class, order = 1, min = 0, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://bar/#b1/2", definedLocally=true, isModifier=false)
+ private List myBar12;
+
+ @Override
+ public boolean isEmpty() {
+ return false; // not implemented
+ }
+
+ public List getBar11() {
+ return myBar11;
+ }
+
+ public List getBar12() {
+ return myBar12;
+ }
+
+ public void setBar11(List theBar11) {
+ myBar11 = theBar11;
+ }
+
+ public void setBar12(List theBar12) {
+ myBar12 = theBar12;
+ }
+
+
+ @Override
+ public BackboneElement copy() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+
+ }
+
+ @Block(name = "Bar2")
+ public static class Bar2 extends BackboneElement {
+
+ @Child(name = "bar121", type = DateType.class, order = 0, min = 0, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://bar/#b1/2/1", definedLocally=true, isModifier=false)
+ private List myBar121;
+
+ @Child(name = "bar122", type = DateType.class, order = 1, min = 0, max = Child.MAX_UNLIMITED)
+ @Extension(url = "http://bar/#b1/2/2", definedLocally=true, isModifier=false)
+ private List myBar122;
+
+ @Override
+ public boolean isEmpty() {
+ return false; // not implemented
+ }
+
+
+ public List getBar121() {
+ return myBar121;
+ }
+
+ public List getBar122() {
+ return myBar122;
+ }
+
+ public void setBar121(List theBar121) {
+ myBar121 = theBar121;
+ }
+
+ public void setBar122(List theBar122) {
+ myBar122 = theBar122;
+ }
+
+ @Override
+ public BackboneElement copy() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false; // not implemented
+ }
+
+
+ @Override
+ public DomainResource copy() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ResourceType getResourceType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/XmlParserHl7OrgDstu2Test.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/XmlParserHl7OrgDstu2Test.java
new file mode 100644
index 00000000000..ea8c81ed73b
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu2hl7org/XmlParserHl7OrgDstu2Test.java
@@ -0,0 +1,1521 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu2hl7org;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import ca.uhn.fhir.context.ConfigurationException;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.api.AddProfileTagEnum;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.narrative.INarrativeGenerator;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.rest.api.Constants;
+import org.apache.commons.io.IOUtils;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.IsNot;
+import org.hamcrest.core.StringContains;
+import org.hamcrest.text.StringContainsInOrder;
+import org.hl7.fhir.instance.model.Address;
+import org.hl7.fhir.instance.model.CodeableConcept;
+import org.hl7.fhir.instance.model.Composition;
+import org.hl7.fhir.instance.model.DiagnosticReport;
+import org.hl7.fhir.instance.model.EnumFactory;
+import org.hl7.fhir.instance.model.Enumeration;
+import org.hl7.fhir.instance.model.InstantType;
+import org.hl7.fhir.instance.model.Narrative;
+import org.hl7.fhir.instance.model.Organization;
+import org.hl7.fhir.instance.model.Patient;
+import org.hl7.fhir.instance.model.PrimitiveType;
+import org.hl7.fhir.instance.model.Reference;
+import org.hl7.fhir.instance.model.Resource;
+import org.hl7.fhir.instance.model.Specimen;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.instance.model.api.INarrative;
+import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.xml.sax.SAXException;
+import org.xmlunit.builder.DiffBuilder;
+import org.xmlunit.builder.Input;
+import org.xmlunit.diff.ComparisonControllers;
+import org.xmlunit.diff.DefaultNodeMatcher;
+import org.xmlunit.diff.Diff;
+import org.xmlunit.diff.ElementSelectors;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_HL7ORG_DSTU2;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class XmlParserHl7OrgDstu2Test {
+
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserHl7OrgDstu2Test.class);
+ private FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_HL7ORG_DSTU2.option(),
+ mavenBundle().groupId("org.xmlunit").artifactId("xmlunit-core").versionAsInProject(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlEnabled() {
+ String tmp = " ";
+ org.hl7.fhir.instance.model.Bundle bundle = (org.hl7.fhir.instance.model.Bundle) ourCtx.newXmlParser().parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ org.hl7.fhir.instance.model.Patient o1 = (org.hl7.fhir.instance.model.Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertEquals("http://lalaland.org", o1Id.getBaseUrl());
+ assertEquals("patient", o1Id.getResourceType());
+ assertEquals("pat1", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnFhirContext() {
+ String tmp = " ";
+ ourCtx.getParserOptions().setOverrideResourceIdWithBundleEntryFullUrl(false);
+ org.hl7.fhir.instance.model.Bundle bundle = (org.hl7.fhir.instance.model.Bundle) ourCtx.newXmlParser().parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ org.hl7.fhir.instance.model.Patient o1 = (org.hl7.fhir.instance.model.Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnParser() {
+ String tmp = " ";
+ org.hl7.fhir.instance.model.Bundle bundle = (org.hl7.fhir.instance.model.Bundle) ourCtx.newXmlParser().setOverrideResourceIdWithBundleEntryFullUrl(false).parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ org.hl7.fhir.instance.model.Patient o1 = (org.hl7.fhir.instance.model.Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ }
+
+ @Test
+ public void testComposition() {
+
+ Composition comp = new Composition();
+ comp.setId("1");
+
+ ourCtx.newXmlParser().encodeResourceToString(comp);
+ ourCtx.newXmlParser().encodeResourceToString(comp);
+ ourCtx.newXmlParser().encodeResourceToString(comp);
+ ourCtx.newXmlParser().encodeResourceToString(comp);
+
+ // comp.
+
+ }
+
+ @Test
+ public void testContainedResourceInExtensionUndeclared() {
+ org.hl7.fhir.instance.model.Patient p = new org.hl7.fhir.instance.model.Patient();
+ p.addName().addFamily("PATIENT");
+
+ Organization o = new Organization();
+ o.setName("ORG");
+ p.addExtension().setUrl("urn:foo").setValue(new org.hl7.fhir.instance.model.Reference(o));
+
+ String str = ourCtx.newXmlParser().encodeResourceToString(p);
+ ourLog.info(str);
+
+ p = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, str);
+ assertEquals("PATIENT", p.getName().get(0).getFamily().get(0).getValue());
+
+ List exts = p.getExtension();
+ assertEquals(1, exts.size());
+ org.hl7.fhir.instance.model.Reference rr = (org.hl7.fhir.instance.model.Reference) exts.get(0).getValue();
+ o = (Organization) rr.getResource();
+ assertEquals("ORG", o.getName());
+ }
+
+ @Test
+ public void testDuplicateContainedResources() {
+
+ org.hl7.fhir.instance.model.Observation resA = new org.hl7.fhir.instance.model.Observation();
+ resA.getCode().setText("A");
+
+ org.hl7.fhir.instance.model.Observation resB = new org.hl7.fhir.instance.model.Observation();
+ resB.getCode().setText("B");
+ resB.addRelated().setTarget(new org.hl7.fhir.instance.model.Reference(resA));
+ resB.addRelated().setTarget(new org.hl7.fhir.instance.model.Reference(resA));
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resB);
+ ourLog.info(encoded);
+
+ assertThat(encoded,
+ stringContainsInOrder(Arrays.asList("", "", " ")));
+ assertThat(encoded, not(stringContainsInOrder(
+ Arrays.asList("", "", ""))));
+
+ }
+
+ @Test
+ public void testEncodeAndParseContained() {
+ IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ // Create an organization, note that the organization does not have an ID
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+
+ // Put the organization as a reference in the patient resource
+ patient.getManagingOrganization().setResource(org);
+
+ String encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, containsString(""));
+ assertThat(encoded, containsString(""));
+
+ // Create a bundle with just the patient resource
+ org.hl7.fhir.instance.model.Bundle b = new org.hl7.fhir.instance.model.Bundle();
+ b.addEntry().setResource(patient);
+
+ // Encode the bundle
+ encoded = xmlParser.encodeResourceToString(b);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "", " ")));
+ assertThat(encoded, containsString(""));
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", " ")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", " ", ""))));
+
+ // Re-parse the bundle
+ patient = (org.hl7.fhir.instance.model.Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient));
+ assertEquals("#1", patient.getManagingOrganization().getReferenceElement().getValue());
+
+ assertNotNull(patient.getManagingOrganization().getResource());
+ org = (Organization) patient.getManagingOrganization().getResource();
+ assertEquals("#1", org.getIdElement().getValue());
+ assertEquals("Contained Test Organization", org.getName());
+
+ // And re-encode a second time
+ encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "",
+ " ", "")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", ""))));
+ assertThat(encoded, containsString(""));
+
+ // And re-encode once more, with the references cleared
+ patient.getContained().clear();
+ patient.getManagingOrganization().setReference(null);
+ encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "",
+ " ", "")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", ""))));
+ assertThat(encoded, containsString(""));
+
+ // And re-encode once more, with the references cleared and a manually set
+ // local ID
+ patient.getContained().clear();
+ patient.getManagingOrganization().setReference(null);
+ patient.getManagingOrganization().getResource().setId(("#333"));
+ encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "",
+ " ", "")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", ""))));
+
+ }
+
+ @Test
+ public void testEncodeAndParseExtensions() throws Exception {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addIdentifier().setUse(org.hl7.fhir.instance.model.Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ org.hl7.fhir.instance.model.Extension ext = new org.hl7.fhir.instance.model.Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new org.hl7.fhir.instance.model.DateTimeType("2011-01-02T11:13:15"));
+ patient.getExtension().add(ext);
+
+ org.hl7.fhir.instance.model.Extension parent = new org.hl7.fhir.instance.model.Extension().setUrl("http://example.com#parent");
+ patient.getExtension().add(parent);
+ org.hl7.fhir.instance.model.Extension child1 = new org.hl7.fhir.instance.model.Extension().setUrl("http://example.com#child").setValue(new org.hl7.fhir.instance.model.StringType("value1"));
+ parent.getExtension().add(child1);
+ org.hl7.fhir.instance.model.Extension child2 = new org.hl7.fhir.instance.model.Extension().setUrl("http://example.com#child").setValue(new org.hl7.fhir.instance.model.StringType("value2"));
+ parent.getExtension().add(child2);
+
+ org.hl7.fhir.instance.model.Extension modExt = new org.hl7.fhir.instance.model.Extension();
+ modExt.setUrl("http://example.com/extensions#modext");
+ modExt.setValue(new org.hl7.fhir.instance.model.DateType("1995-01-02"));
+ patient.getModifierExtension().add(modExt);
+
+ org.hl7.fhir.instance.model.HumanName name = patient.addName();
+ name.addFamily("Blah");
+ org.hl7.fhir.instance.model.StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ org.hl7.fhir.instance.model.Extension ext2 = new org.hl7.fhir.instance.model.Extension().setUrl("http://examples.com#givenext").setValue(new org.hl7.fhir.instance.model.StringType("given"));
+ given.getExtension().add(ext2);
+
+ org.hl7.fhir.instance.model.StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ org.hl7.fhir.instance.model.Extension given2ext = new org.hl7.fhir.instance.model.Extension().setUrl("http://examples.com#givenext_parent");
+ given2.getExtension().add(given2ext);
+ given2ext.addExtension().setUrl("http://examples.com#givenext_child").setValue(new org.hl7.fhir.instance.model.StringType("CHILD"));
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+
+ /*
+ * Now parse this back
+ */
+
+ org.hl7.fhir.instance.model.Patient parsed = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, enc);
+ ext = parsed.getExtension().get(0);
+ assertEquals("http://example.com/extensions#someext", ext.getUrl());
+ assertEquals("2011-01-02T11:13:15", ((org.hl7.fhir.instance.model.DateTimeType) ext.getValue()).getValueAsString());
+
+ parent = patient.getExtension().get(1);
+ assertEquals("http://example.com#parent", parent.getUrl());
+ assertNull(parent.getValue());
+ child1 = parent.getExtension().get(0);
+ assertEquals("http://example.com#child", child1.getUrl());
+ assertEquals("value1", ((org.hl7.fhir.instance.model.StringType) child1.getValue()).getValueAsString());
+ child2 = parent.getExtension().get(1);
+ assertEquals("http://example.com#child", child2.getUrl());
+ assertEquals("value2", ((org.hl7.fhir.instance.model.StringType) child2.getValue()).getValueAsString());
+
+ modExt = parsed.getModifierExtension().get(0);
+ assertEquals("http://example.com/extensions#modext", modExt.getUrl());
+ assertEquals("1995-01-02", ((org.hl7.fhir.instance.model.DateType) modExt.getValue()).getValueAsString());
+
+ name = parsed.getName().get(0);
+
+ ext2 = name.getGiven().get(0).getExtension().get(0);
+ assertEquals("http://examples.com#givenext", ext2.getUrl());
+ assertEquals("given", ((org.hl7.fhir.instance.model.StringType) ext2.getValue()).getValueAsString());
+
+ given2ext = name.getGiven().get(1).getExtension().get(0);
+ assertEquals("http://examples.com#givenext_parent", given2ext.getUrl());
+ assertNull(given2ext.getValue());
+ org.hl7.fhir.instance.model.Extension given2ext2 = given2ext.getExtension().get(0);
+ assertEquals("http://examples.com#givenext_child", given2ext2.getUrl());
+ assertEquals("CHILD", ((org.hl7.fhir.instance.model.StringType) given2ext2.getValue()).getValue());
+
+ }
+
+ @Test
+ public void testEncodeBinaryResource() {
+
+ org.hl7.fhir.instance.model.Binary patient = new org.hl7.fhir.instance.model.Binary();
+ patient.setContentType("foo");
+ patient.setContent(new byte[] { 1, 2, 3, 4 });
+
+ String val = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertEquals(
+ " ",
+ val);
+
+ }
+
+ @Test
+ public void testEncodeBinaryWithNoContentType() {
+ org.hl7.fhir.instance.model.Binary b = new org.hl7.fhir.instance.model.Binary();
+ b.setContent(new byte[] { 1, 2, 3, 4 });
+
+ String output = ourCtx.newXmlParser().encodeResourceToString(b);
+ ourLog.info(output);
+
+ assertEquals(" ", output);
+ }
+
+ @Test
+ public void testEncodeBoundCode() {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addAddress().setUse(Address.AddressUse.HOME);
+
+ patient.getGenderElement().setValue(org.hl7.fhir.instance.model.Enumerations.AdministrativeGender.MALE);
+
+ String val = ourCtx.newXmlParser().encodeResourceToString(patient);
+ ourLog.info(val);
+
+ assertThat(val, containsString("home"));
+ assertThat(val, containsString("male"));
+ }
+
+ @Test
+ public void testEncodeBundle() throws InterruptedException {
+ org.hl7.fhir.instance.model.Bundle b = new org.hl7.fhir.instance.model.Bundle();
+ b.getMeta().addTag().setSystem("http://hl7.org/fhir/tag").setCode("http://hl7.org/fhir/tag/message")
+ .setDisplay("Message");
+
+ InstantType pub = InstantType.withCurrentTime();
+ b.getMeta().setLastUpdatedElement(pub);
+
+ org.hl7.fhir.instance.model.Patient p1 = new org.hl7.fhir.instance.model.Patient();
+ p1.addName().addFamily("Family1");
+ org.hl7.fhir.instance.model.Bundle.BundleEntryComponent entry = b.addEntry();
+ p1.getIdElement().setValue("1");
+ entry.setResource(p1);
+
+ org.hl7.fhir.instance.model.Patient p2 = new org.hl7.fhir.instance.model.Patient();
+ p2.addName().addFamily("Family2");
+ entry = b.addEntry();
+ p2.getIdElement().setValue("2");
+ entry.setResource(p2);
+
+ String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(bundleString);
+
+ // @formatter:on
+ String[] strings = { "",
+ " ", "",
+ "", "", "" };
+ // @formatter:off
+
+ assertThat(bundleString, stringContainsInOrder(strings));
+ }
+
+ @Test
+ public void testEncodeBundleCategory() {
+
+ org.hl7.fhir.instance.model.Bundle b = new org.hl7.fhir.instance.model.Bundle();
+ org.hl7.fhir.instance.model.Bundle.BundleEntryComponent e = b.addEntry();
+ e.setResource(new org.hl7.fhir.instance.model.Patient());
+ e.getResource().getMeta().addTag().setSystem("scheme").setCode("term").setDisplay("label");
+
+ String val = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(val);
+
+ // @formatter:off
+ assertThat(val, stringContainsInOrder("", "", "",
+ "", "
"));
+ // @formatter:on
+
+ b = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Bundle.class, val);
+ assertEquals(1, b.getEntry().size());
+ assertEquals(1, b.getEntry().get(0).getResource().getMeta().getTag().size());
+ assertEquals("scheme", b.getEntry().get(0).getResource().getMeta().getTag().get(0).getSystem());
+ assertEquals("term", b.getEntry().get(0).getResource().getMeta().getTag().get(0).getCode());
+ assertEquals("label", b.getEntry().get(0).getResource().getMeta().getTag().get(0).getDisplay());
+ }
+
+ private Matcher super String> stringContainsInOrder(java.lang.String... substrings) {
+ return Matchers.stringContainsInOrder(Arrays.asList(substrings));
+ }
+
+ private Matcher super String> stringContainsInOrder(List substrings) {
+ return Matchers.stringContainsInOrder(substrings);
+ }
+
+ @Test
+ public void testEncodeContainedAndIncludedResources() {
+
+ DiagnosticReport rpt = new DiagnosticReport();
+ rpt.getCode().setText("Report");
+
+ Specimen spm = new Specimen();
+ spm.addIdentifier().setValue("Report1ContainedSpecimen1");
+ rpt.addSpecimen().setResource(spm);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String str = p.encodeResourceToString(rpt);
+
+ ourLog.info(str);
+
+ }
+
+ @Test
+ public void testEncodeContainedResources() throws Exception {
+
+ DiagnosticReport rpt = new DiagnosticReport();
+ Specimen spm = new Specimen();
+ spm.addIdentifier().setSystem("urn").setValue("123");
+ rpt.getText().setDivAsString("AAA");
+ rpt.addSpecimen().setResource(spm);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String str = p.encodeResourceToString(rpt);
+
+ ourLog.info(str);
+ assertThat(str, StringContains.containsString("AAA"));
+ assertThat(str, StringContains.containsString("reference value=\"#"));
+
+ int idx = str.indexOf("reference value=\"#") + "reference value=\"#".length();
+ int idx2 = str.indexOf('"', idx + 1);
+ String id = str.substring(idx, idx2);
+ assertThat(str, stringContainsInOrder("", " "));
+ assertThat(str, IsNot.not(StringContains.containsString("")));
+
+ }
+
+ @Test
+ public void testEncodeContainedWithNarrativeIsSuppresed() throws Exception {
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ // Create an organization, note that the organization does not have an ID
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+ org.getText().setDivAsString("FOOBAR");
+
+ // Create a patient
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+ patient.getText().setDivAsString("BARFOO");
+ patient.getManagingOrganization().setResource(org);
+
+ String encoded = parser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder("",
+ "BARFOO", "", "", "", " "));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser
+ .parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(Address.AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Address ref = actual.getFoo();
+ assertEquals("line1", ref.getLine().get(0).getValue());
+
+ }
+
+ @Test
+ public void testEncodeDeclaredExtensionWithResourceContent() {
+ IParser parser = ourCtx.newXmlParser();
+
+ MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
+ patient.addAddress().setUse(Address.AddressUse.HOME);
+ patient.setFoo(new org.hl7.fhir.instance.model.Reference("Organization/123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(
+ " "));
+
+ MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
+ assertEquals(Address.AddressUse.HOME, patient.getAddress().get(0).getUse());
+ org.hl7.fhir.instance.model.Reference ref = actual.getFoo();
+ assertEquals("Organization/123", ref.getReferenceElement().getValue());
+
+ }
+
+ /**
+ * #158
+ */
+ @Test
+ public void testEncodeEmptyTag() {
+ org.hl7.fhir.instance.model.Patient p = new org.hl7.fhir.instance.model.Patient();
+ p.getMeta().addTag();
+
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(p);
+ assertThat(encoded, not(containsString("tag")));
+
+ // With tag
+
+ p = new org.hl7.fhir.instance.model.Patient();
+ p.getMeta().addTag().setSystem("sys").setCode("code");
+
+ encoded = ourCtx.newXmlParser().encodeResourceToString(p);
+ assertThat(encoded, (containsString("tag")));
+ }
+
+ @Test
+ public void testEncodeEscapedChars() {
+
+ org.hl7.fhir.instance.model.Patient p = new org.hl7.fhir.instance.model.Patient();
+ p.addName().addFamily("and <>&ü");
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(p);
+ ourLog.info(enc);
+
+ p = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, enc);
+ assertEquals("and <>&ü", p.getName().get(0).getFamily().get(0).getValue());
+
+ p = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class,
+ " ");
+ assertEquals("quot \"", p.getName().get(0).getFamily().get(0).getValue());
+
+ }
+
+ @Test
+ public void testEncodeEscapedExtendedChars() {
+ org.hl7.fhir.instance.model.Patient p = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class,
+ " ");
+ assertEquals("uuml ü", p.getName().get(0).getFamily().get(0).getValue());
+ }
+
+ @Test
+ public void testEncodeExtensionUndeclaredNonModifier() {
+ org.hl7.fhir.instance.model.Observation obs = new org.hl7.fhir.instance.model.Observation();
+ obs.setId("1");
+ obs.getMeta().addProfile("http://profile");
+ org.hl7.fhir.instance.model.Extension ext = obs.addExtension();
+ ext.setUrl("http://exturl").setValue(new org.hl7.fhir.instance.model.StringType("ext_url_value"));
+
+ obs.getCode().setText("CODE");
+
+ IParser parser = ourCtx.newXmlParser();
+
+ String output = parser.setPrettyPrint(true).encodeResourceToString(obs);
+ ourLog.info(output);
+
+ // @formatter:off
+ assertThat(output, stringContainsInOrder("", "", "",
+ "", "", ""));
+ assertThat(output, not(stringContainsInOrder("")));
+ // @formatter:on
+
+ obs = parser.parseResource(org.hl7.fhir.instance.model.Observation.class, output);
+ assertEquals(1, obs.getExtension().size());
+ assertEquals("http://exturl", obs.getExtension().get(0).getUrl());
+ assertEquals("ext_url_value", ((org.hl7.fhir.instance.model.StringType) obs.getExtension().get(0).getValue()).getValue());
+ }
+
+ @Test
+ public void testEncodeExtensionUndeclaredNonModifierWithChildExtension() {
+ org.hl7.fhir.instance.model.Observation obs = new org.hl7.fhir.instance.model.Observation();
+ obs.setId("1");
+ obs.getMeta().addProfile("http://profile");
+ org.hl7.fhir.instance.model.Extension ext = obs.addExtension();
+ ext.setUrl("http://exturl");
+
+ org.hl7.fhir.instance.model.Extension subExt = ext.addExtension();
+ subExt.setUrl("http://subext").setValue(new org.hl7.fhir.instance.model.StringType("sub_ext_value"));
+
+ obs.getCode().setText("CODE");
+
+ IParser parser = ourCtx.newXmlParser();
+
+ String output = parser.setPrettyPrint(true).encodeResourceToString(obs);
+ ourLog.info(output);
+
+ // @formatter:off
+ assertThat(output,
+ stringContainsInOrder("", "", "",
+ "", "",
+ "", ""));
+ assertThat(output, not(stringContainsInOrder("")));
+ // @formatter:on
+
+ obs = parser.parseResource(org.hl7.fhir.instance.model.Observation.class, output);
+ assertEquals(1, obs.getExtension().size());
+ assertEquals("http://exturl", obs.getExtension().get(0).getUrl());
+ assertEquals(1, obs.getExtension().get(0).getExtension().size());
+ assertEquals("http://subext", obs.getExtension().get(0).getExtension().get(0).getUrl());
+ assertEquals("sub_ext_value", ((org.hl7.fhir.instance.model.StringType) obs.getExtension().get(0).getExtension().get(0).getValue()).getValue());
+ }
+
+ /**
+ * See #327
+ */
+ @Test
+ public void testEncodeExtensionWithContainedResource() {
+
+ TestPatientFor327 patient = new TestPatientFor327();
+ patient.setBirthDateElement(new org.hl7.fhir.instance.model.DateType("2016-04-14"));
+
+ List conditions = new ArrayList();
+ org.hl7.fhir.instance.model.Condition condition = new org.hl7.fhir.instance.model.Condition();
+ condition.addBodySite().setText("BODY SITE");
+ conditions.add(new org.hl7.fhir.instance.model.Reference(condition));
+ patient.setCondition(conditions);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ " "
+ ));
+ //@formatter:on
+ }
+
+ @Test
+ public void testEncodeExtensionWithResourceContent() {
+ IParser parser = ourCtx.newXmlParser();
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addAddress().setUse(Address.AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue(new org.hl7.fhir.instance.model.Reference().setReference("Organization/123"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(
+ " "));
+
+ org.hl7.fhir.instance.model.Patient actual = parser.parseResource(org.hl7.fhir.instance.model.Patient.class, val);
+ assertEquals(Address.AddressUse.HOME, patient.getAddress().get(0).getUse());
+ List ext = actual.getExtension();
+ assertEquals(1, ext.size());
+ org.hl7.fhir.instance.model.Reference ref = (org.hl7.fhir.instance.model.Reference) ext.get(0).getValue();
+ assertEquals("Organization/123", ref.getReferenceElement().getValue());
+
+ }
+
+ @Test
+ public void testEncodeInvalidChildGoodException() {
+ org.hl7.fhir.instance.model.Observation obs = new org.hl7.fhir.instance.model.Observation();
+ obs.setValue(new org.hl7.fhir.instance.model.DecimalType(112.22));
+
+ IParser p = ourCtx.newJsonParser();
+
+ try {
+ p.encodeResourceToString(obs);
+ } catch (DataFormatException e) {
+ assertThat(e.getMessage(), StringContains.containsString("DecimalType"));
+ }
+ }
+
+ @Test
+ public void testEncodeNarrativeSuppressed() throws Exception {
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().addFamily("FAMILY");
+ patient.getMaritalStatus().addCoding().setCode("D");
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).setSuppressNarratives(true)
+ .encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("", " ",
+ "
", ""));
+ assertThat(encoded, not(containsString("text")));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, containsString("maritalStatus"));
+ }
+
+ @Test
+ public void testEncodeNonContained() {
+ // Create an organization
+ Organization org = new Organization();
+ org.setId("Organization/65546");
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+ patient.getManagingOrganization().setResource(org);
+
+ // Create a list containing both resources. In a server method, you might
+ // just
+ // return this list, but here we will create a bundle to encode.
+ List resources = new ArrayList();
+ resources.add(org);
+ resources.add(patient);
+
+ // Create a bundle with both
+ org.hl7.fhir.instance.model.Bundle b = new org.hl7.fhir.instance.model.Bundle();
+ b.addEntry().setResource(org);
+ b.addEntry().setResource(patient);
+
+ // Encode the buntdle
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(encoded);
+ assertThat(encoded, not(containsString("")));
+ assertThat(encoded, stringContainsInOrder("", " "));
+ assertThat(encoded, containsString(""));
+ assertThat(encoded, stringContainsInOrder("", " "));
+
+ encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, not(containsString("")));
+ assertThat(encoded, containsString(""));
+
+ }
+
+ @Test
+ public void testEncodePrettyPrint() throws Exception {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.getText().setDivAsString("\n hello \n LINE1\n LINE2
\n\n\n\n");
+ patient.addName().addFamily("Family").addGiven("Given");
+
+ // @formatter:off
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(patient);
+ ourLog.info(encoded);
+ /*
+ * Note at least one space is placed where any whitespace was, as it is hard
+ * to tell what whitespace had no purpose
+ */
+ String expected = ""
+ + " hello " + "\n LINE1\n LINE2
"
+ + " ";
+ assertEquals(expected, encoded);
+
+ encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+ expected = "\n" + " \n"
+ + " \n" + " hello \n"
+ + " \n LINE1\n LINE2
\n" + " \n" + " \n" + " \n"
+ + " \n" + " \n" + " \n" + " \n"
+ + " ";
+ // @formatter:on
+
+ // Whitespace should be preserved and not reformatted in narrative blocks
+ assertEquals(expected, encoded);
+
+ }
+
+ @Test
+ public void testEncodeResourceRef() throws DataFormatException {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.setManagingOrganization(new org.hl7.fhir.instance.model.Reference());
+
+ IParser p = ourCtx.newXmlParser();
+ String str = p.encodeResourceToString(patient);
+ assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
+
+ org.hl7.fhir.instance.model.Reference ref = new org.hl7.fhir.instance.model.Reference();
+ ref.setReference("Organization/123");
+ ref.setDisplay("DISPLAY!");
+ patient.setManagingOrganization(ref);
+ str = p.encodeResourceToString(patient);
+ assertThat(str, StringContains.containsString(
+ " "));
+
+ Organization org = new Organization();
+ org.addIdentifier().setSystem("foo").setValue("bar");
+ patient.setManagingOrganization(new org.hl7.fhir.instance.model.Reference(org));
+ str = p.encodeResourceToString(patient);
+ assertThat(str, StringContains.containsString("THE DIV
", ""));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeSummary2() throws Exception {
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("", ""));
+ assertThat(encoded, stringContainsInOrder("", " ",
+ "
", " "));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeUndeclaredExtensionWithAddressContent() {
+ IParser parser = ourCtx.newXmlParser();
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addAddress().setUse(Address.AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue(new Address().addLine("line1"));
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains
+ .containsString(" "));
+
+ MyPatientWithOneDeclaredAddressExtension actual = parser
+ .parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
+ assertEquals(Address.AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Address ref = actual.getFoo();
+ assertEquals("line1", ref.getLine().get(0).getValue());
+
+ }
+
+ @Test
+ public void testEncodeUndeclaredExtensionWithEnumerationContent() {
+ IParser parser = ourCtx.newXmlParser();
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addAddress().setUse(Address.AddressUse.HOME);
+ EnumFactory fact = new Address.AddressUseEnumFactory();
+ PrimitiveType enumeration = new Enumeration(fact).setValue(Address.AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue(enumeration);
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val,
+ StringContains.containsString(" "));
+
+ MyPatientWithOneDeclaredEnumerationExtension actual = parser
+ .parseResource(MyPatientWithOneDeclaredEnumerationExtension.class, val);
+ assertEquals(Address.AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Enumeration ref = actual.getFoo();
+ assertEquals("home", ref.getValue().toCode());
+
+ }
+
+ @Test
+ public void testEncodingNullExtension() {
+ org.hl7.fhir.instance.model.Patient p = new org.hl7.fhir.instance.model.Patient();
+ org.hl7.fhir.instance.model.Extension extension = new org.hl7.fhir.instance.model.Extension().setUrl("http://foo#bar");
+ p.getExtension().add(extension);
+ String str = ourCtx.newXmlParser().encodeResourceToString(p);
+
+ assertEquals("", str);
+
+ extension.setValue(new org.hl7.fhir.instance.model.StringType());
+
+ str = ourCtx.newXmlParser().encodeResourceToString(p);
+ assertEquals("", str);
+
+ extension.setValue(new org.hl7.fhir.instance.model.StringType(""));
+
+ str = ourCtx.newXmlParser().encodeResourceToString(p);
+ assertEquals("", str);
+
+ }
+
+ @Test
+ public void testExtensionOnComposite() throws Exception {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+
+ org.hl7.fhir.instance.model.HumanName name = patient.addName();
+ name.addFamily("Shmoe");
+ org.hl7.fhir.instance.model.HumanName given = name.addGiven("Joe");
+ org.hl7.fhir.instance.model.Extension ext2 = new org.hl7.fhir.instance.model.Extension().setUrl("http://examples.com#givenext").setValue(new org.hl7.fhir.instance.model.StringType("Hello"));
+ given.getExtension().add(ext2);
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(
+ " "));
+
+ org.hl7.fhir.instance.model.Patient parsed = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, new StringReader(enc));
+ assertEquals(1, parsed.getName().get(0).getExtension().size());
+ org.hl7.fhir.instance.model.Extension ext = parsed.getName().get(0).getExtension().get(0);
+ assertEquals("Hello", ((IPrimitiveType>) ext.getValue()).getValue());
+
+ }
+
+ @Test
+ public void testExtensionOnPrimitive() throws Exception {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+
+ org.hl7.fhir.instance.model.HumanName name = patient.addName();
+ org.hl7.fhir.instance.model.StringType family = name.addFamilyElement();
+ family.setValue("Shmoe");
+
+ org.hl7.fhir.instance.model.Extension ext2 = new org.hl7.fhir.instance.model.Extension().setUrl("http://examples.com#givenext").setValue(new org.hl7.fhir.instance.model.StringType("Hello"));
+ family.getExtension().add(ext2);
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(
+ " "));
+
+ org.hl7.fhir.instance.model.Patient parsed = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, new StringReader(enc));
+ assertEquals(1, parsed.getName().get(0).getFamily().get(0).getExtension().size());
+ org.hl7.fhir.instance.model.Extension ext = parsed.getName().get(0).getFamily().get(0).getExtension().get(0);
+ assertEquals("Hello", ((IPrimitiveType>) ext.getValue()).getValue());
+
+ }
+
+ @Test
+ public void testExtensions() throws DataFormatException {
+
+ MyPatientHl7Org patient = new MyPatientHl7Org();
+ patient.setPetName(new org.hl7.fhir.instance.model.StringType("Fido"));
+ patient.getImportantDates().add(new org.hl7.fhir.instance.model.DateTimeType("2010-01-02"));
+ patient.getImportantDates().add(new org.hl7.fhir.instance.model.DateTimeType("2014-01-26T11:11:11"));
+
+ patient.addName().addFamily("Smith");
+
+ IParser p = ourCtx.newXmlParser();
+ String str = p.encodeResourceToString(patient);
+
+ ourLog.info(str);
+
+ assertThat(str, StringContains.containsString(""));
+ assertThat(str, StringContains.containsString(
+ " "));
+ assertThat(str, StringContains.containsString(
+ " "));
+ assertThat(str, StringContains.containsString(
+ " "));
+ assertThat(str, StringContains.containsString(" "));
+
+ }
+
+ @Test
+ public void testLoadAndAncodeMessage() throws Exception {
+
+ // @formatter:off
+ String msg = ""
+ + "John Cardinal: 444333333 "
+ + " "
+ + ""
+ + " "
+ + " "
+ + " "
+ + "" + ""
+ + " ";
+ // @formatter:on
+
+ org.hl7.fhir.instance.model.Patient patient = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, msg);
+
+ assertEquals(Narrative.NarrativeStatus.GENERATED, patient.getText().getStatus());
+ assertThat(patient.getText().getDiv().getValueAsString(), containsString(">John Cardinal: 444333333 <"));
+ assertEquals("PRP1660", patient.getIdentifier().get(0).getValue());
+
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
+
+ ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
+
+ ourLog.info("Expected: {}", msg);
+ ourLog.info("Actual: {}", encoded);
+
+ compareXml(msg, encoded);
+
+ }
+
+ @Test
+ public void testLoadAndEncodeDeclaredExtensions()
+ throws ConfigurationException, DataFormatException, SAXException, IOException {
+ IParser p = ourCtx.newXmlParser();
+ ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER);
+
+ // @formatter:off
+ String msg = "\n" + " \n"
+ + " \n" + " \n" + " \n"
+ + " \n" + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n" + " \n" + " \n"
+ + " ";
+ // @formatter:on
+
+ ResourceWithExtensionsA resource = (ResourceWithExtensionsA) p.parseResource(ResourceWithExtensionsA.class, msg);
+ assertEquals("IdentifierLabel", resource.getIdentifier().get(0).getValue());
+ assertEquals("Foo1Value", resource.getFoo1().get(0).getValue());
+ assertEquals("Foo1Value2", resource.getFoo1().get(1).getValue());
+ assertEquals("Foo2Value1", resource.getFoo2().getValue());
+ assertEquals("2013-01-01", resource.getBar1().get(0).getBar11().get(0).getValueAsString());
+ assertEquals("2013-01-02", resource.getBar1().get(0).getBar12().get(0).getBar121().get(0).getValueAsString());
+ assertEquals("2013-01-12", resource.getBar1().get(0).getBar12().get(0).getBar121().get(1).getValueAsString());
+ assertEquals("2013-01-03", resource.getBar1().get(0).getBar12().get(0).getBar122().get(0).getValueAsString());
+
+ String encoded = p.setPrettyPrint(true).encodeResourceToString(resource);
+ ourLog.info(encoded);
+
+ compareXml(msg, encoded);
+ }
+
+ @Test
+ public void testLoadAndEncodeUndeclaredExtensions()
+ throws ConfigurationException, DataFormatException, SAXException, IOException {
+ IParser p = ourCtx.newXmlParser();
+
+ // @formatter:off
+ String msg = "\n" + " \n"
+ + " \n" + " \n" + " \n"
+ + " \n" + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n" + " \n"
+ + " \n" + " \n"
+ + " \n" + " \n" + " \n" + " \n"
+ + " ";
+ // @formatter:on
+
+ org.hl7.fhir.instance.model.Patient resource = (org.hl7.fhir.instance.model.Patient) p.parseResource(msg);
+ assertEquals("IdentifierLabel", resource.getIdentifier().get(0).getValue());
+ assertEquals("Foo1Value", ((IPrimitiveType>) resource.getExtension().get(0).getValue()).getValueAsString());
+ assertEquals("Foo1Value2", ((IPrimitiveType>) resource.getExtension().get(1).getValue()).getValueAsString());
+ assertEquals("Foo2Value1",
+ ((IPrimitiveType>) resource.getModifierExtension().get(0).getValue()).getValueAsString());
+
+ assertEquals("2013-01-01",
+ ((IPrimitiveType>) resource.getExtension().get(2).getExtension().get(0).getValue()).getValueAsString());
+ assertEquals("2013-01-02",
+ ((IPrimitiveType>) resource.getExtension().get(2).getExtension().get(1).getExtension().get(0).getValue())
+ .getValueAsString());
+
+ String encoded = p.encodeResourceToString(resource);
+ ourLog.info(encoded);
+
+ compareXml(msg, encoded);
+ }
+
+ @Test
+ public void testMoreExtensions() throws Exception {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addIdentifier().setUse(org.hl7.fhir.instance.model.Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ org.hl7.fhir.instance.model.Extension ext = new org.hl7.fhir.instance.model.Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new org.hl7.fhir.instance.model.DateTimeType("2011-01-02T11:13:15"));
+
+ // Add the extension to the resource
+ patient.getExtension().add(ext);
+ // END SNIPPET: resourceExtension
+
+ // START SNIPPET: resourceStringExtension
+ org.hl7.fhir.instance.model.HumanName name = patient.addName();
+ name.addFamily("Shmoe");
+ org.hl7.fhir.instance.model.StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ org.hl7.fhir.instance.model.Extension ext2 = new org.hl7.fhir.instance.model.Extension().setUrl("http://examples.com#givenext").setValue(new org.hl7.fhir.instance.model.StringType("given"));
+ given.getExtension().add(ext2);
+
+ org.hl7.fhir.instance.model.StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ org.hl7.fhir.instance.model.Extension given2ext = new org.hl7.fhir.instance.model.Extension().setUrl("http://examples.com#givenext_parent");
+ given2.getExtension().add(given2ext);
+ given2ext.addExtension().setUrl("http://examples.com#givenext_child").setValue(new org.hl7.fhir.instance.model.StringType("CHILD"));
+ // END SNIPPET: resourceStringExtension
+
+ // START SNIPPET: subExtension
+ org.hl7.fhir.instance.model.Extension parent = new org.hl7.fhir.instance.model.Extension().setUrl("http://example.com#parent");
+ patient.getExtension().add(parent);
+
+ org.hl7.fhir.instance.model.Extension child1 = new org.hl7.fhir.instance.model.Extension().setUrl("http://example.com#child").setValue(new org.hl7.fhir.instance.model.StringType("value1"));
+ parent.getExtension().add(child1);
+
+ org.hl7.fhir.instance.model.Extension child2 = new org.hl7.fhir.instance.model.Extension().setUrl("http://example.com#child").setValue(new org.hl7.fhir.instance.model.StringType("value1"));
+ parent.getExtension().add(child2);
+ // END SNIPPET: subExtension
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(
+ " "));
+ }
+
+ // Narrative generation not currently supported for HL7org structures
+ public void testNarrativeGeneration() throws DataFormatException, IOException {
+
+ org.hl7.fhir.instance.model.Patient patient = new org.hl7.fhir.instance.model.Patient();
+ patient.addName().addFamily("Smith");
+ Organization org = new Organization();
+ patient.getManagingOrganization().setResource(org);
+
+ INarrativeGenerator gen = new INarrativeGenerator() {
+
+ @Override
+ public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
+ try {
+ theNarrative.setDivAsString("help");
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ theNarrative.setStatusAsString("generated");
+ }
+
+ };
+
+ FhirContext context = ourCtx;
+ context.setNarrativeGenerator(gen);
+ IParser p = context.newXmlParser();
+ String str = p.encodeResourceToString(patient);
+
+ ourLog.info(str);
+
+ assertThat(str, StringContains.containsString(",\"text\":{\"status\":\"generated\",\"div\":\"help\"},"));
+ }
+
+ @Test
+ public void testNestedContainedResources() {
+
+ org.hl7.fhir.instance.model.Observation A = new org.hl7.fhir.instance.model.Observation();
+ A.getCode().setText("A");
+
+ org.hl7.fhir.instance.model.Observation B = new org.hl7.fhir.instance.model.Observation();
+ B.getCode().setText("B");
+ A.addRelated().setTarget(new org.hl7.fhir.instance.model.Reference(B));
+
+ org.hl7.fhir.instance.model.Observation C = new org.hl7.fhir.instance.model.Observation();
+ C.getCode().setText("C");
+ B.addRelated().setTarget(new org.hl7.fhir.instance.model.Reference(C));
+
+ String str = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(A);
+ ourLog.info(str);
+
+ assertThat(str,
+ stringContainsInOrder(Arrays.asList("", "", "")));
+ assertThat(str, stringContainsInOrder(Arrays.asList("", " ", "", " ")));
+
+ org.hl7.fhir.instance.model.Observation obs = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Observation.class, str);
+ assertEquals("A", obs.getCode().getText());
+
+ org.hl7.fhir.instance.model.Observation obsB = (org.hl7.fhir.instance.model.Observation) obs.getRelated().get(0).getTarget().getResource();
+ assertEquals("B", obsB.getCode().getText());
+
+ org.hl7.fhir.instance.model.Observation obsC = (org.hl7.fhir.instance.model.Observation) obsB.getRelated().get(0).getTarget().getResource();
+ assertEquals("C", obsC.getCode().getText());
+
+ }
+
+ @Test
+ public void testParseBinaryResource() {
+
+ org.hl7.fhir.instance.model.Binary val = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Binary.class,
+ " ");
+ assertEquals("foo", val.getContentType());
+ assertArrayEquals(new byte[] { 1, 2, 3, 4 }, val.getContent());
+
+ }
+
+ /**
+ * Thanks to Alexander Kley!
+ */
+ @Test
+ public void testParseContainedBinaryResource() throws Exception {
+ byte[] bin = new byte[] { 0, 1, 2, 3, 4 };
+ final org.hl7.fhir.instance.model.Binary binary = new org.hl7.fhir.instance.model.Binary();
+ binary.setContentType("PatientConsent").setContent(bin);
+ // binary.setId(UUID.randomUUID().toString());
+
+ org.hl7.fhir.instance.model.DocumentManifest manifest = new org.hl7.fhir.instance.model.DocumentManifest();
+ // manifest.setId(UUID.randomUUID().toString());
+ CodeableConcept cc = new CodeableConcept();
+ cc.addCoding().setSystem("mySystem").setCode("PatientDocument");
+ manifest.setType(cc);
+ manifest.setMasterIdentifier(new org.hl7.fhir.instance.model.Identifier().setSystem("mySystem").setValue(UUID.randomUUID().toString()));
+ manifest.addContent().setP(new org.hl7.fhir.instance.model.Reference(binary));
+ manifest.setStatus(org.hl7.fhir.instance.model.Enumerations.DocumentReferenceStatus.CURRENT);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(manifest);
+ ourLog.info(encoded);
+ assertThat(encoded,
+ StringContainsInOrder.stringContainsInOrder(Arrays.asList("contained>", "")));
+
+ org.hl7.fhir.instance.model.DocumentManifest actual = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.DocumentManifest.class, encoded);
+ assertEquals(1, actual.getContained().size());
+ assertEquals(1, actual.getContent().size());
+
+ /*
+ * If this fails, the child named "p" in DocumentManifest is missing the
+ * type IBaseResource in its definition... This isn't being auto added right
+ * now, need to figure out why
+ *
+ * @Child(name = "p", type = {Attachment.class, IBaseResource.class},
+ * order=1, min=1, max=1, modifier=false, summary=true)
+ */
+ assertNotNull(actual.getContent().get(0).getPReference().getResource());
+
+ }
+
+ @Test
+ public void testParseEncodeNarrative() {
+
+ String input = " Donald null DUCK Identifier 7000135 Address 10 Duxon Street
VICTORIA BC Can Date of birth 01 June 1980
";
+ IBaseResource res = ourCtx.newXmlParser().parseResource(input);
+
+ String output = ourCtx.newXmlParser().encodeResourceToString(res);
+
+ // Should occur exactly twice (once for the resource, once for the DIV
+ assertThat(output, (StringContainsInOrder.stringContainsInOrder(Arrays.asList("Patient xmlns", "div xmlns"))));
+ assertThat(output, not(StringContainsInOrder.stringContainsInOrder(Arrays.asList("b xmlns"))));
+
+ output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
+
+ // Should occur exactly twice (once for the resource, once for the DIV
+ assertThat(output, (StringContainsInOrder.stringContainsInOrder(Arrays.asList("Patient xmlns", "div xmlns"))));
+ assertThat(output, not(StringContainsInOrder.stringContainsInOrder(Arrays.asList("b xmlns"))));
+
+ }
+
+ @Test
+ public void testParseLanguage() {
+ String input = " 海生 王 Identifier URNo Address 99 Houston Road
BENTLEIGH Victoria Date of birth 01 January 1997
";
+ org.hl7.fhir.instance.model.Patient pt = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, input);
+
+ assertEquals("zh-CN", pt.getLanguage());
+ }
+
+ @Test
+ public void testParseWithXmlHeader() throws ConfigurationException, DataFormatException {
+ IParser p = ourCtx.newXmlParser();
+
+ // @formatter:off
+ String msg = "" + "\n"
+ + " \n" + " \n" + " \n" + " ";
+ // @formatter:on
+
+ org.hl7.fhir.instance.model.Patient resource = (org.hl7.fhir.instance.model.Patient) p.parseResource(msg);
+ assertEquals("IdentifierLabel", resource.getIdentifier().get(0).getValue());
+ }
+
+ @Test
+ public void testReEncode() throws SAXException, IOException {
+
+ // @formatter:off
+ String msg = ""
+ + " "
+ + " ";
+ // @formatter:on
+
+ org.hl7.fhir.instance.model.Patient patient1 = ourCtx.newXmlParser().parseResource(org.hl7.fhir.instance.model.Patient.class, msg);
+ String encoded1 = ourCtx.newXmlParser().encodeResourceToString(patient1);
+
+ ourLog.info("Expected: {}", msg);
+ ourLog.info("Actual: {}", encoded1);
+
+ compareXml(msg, encoded1);
+ }
+
+ @Test
+ public void testSimpleResourceEncode() throws IOException, SAXException {
+
+ String xmlString = IOUtils.toString(
+ XmlParserHl7OrgDstu2Test.class.getResourceAsStream("/example-patient-general-hl7orgdstu2.json"),
+ Charset.forName("UTF-8"));
+ org.hl7.fhir.instance.model.Patient obs = ourCtx.newJsonParser().parseResource(org.hl7.fhir.instance.model.Patient.class, xmlString);
+
+ List undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getExtension();
+ org.hl7.fhir.instance.model.Extension undeclaredExtension = undeclaredExtensions.get(0);
+ assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl());
+
+ ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
+
+ IParser jsonParser = ourCtx.newXmlParser();
+ String encoded = jsonParser.encodeResourceToString(obs);
+ ourLog.info(encoded);
+
+ String jsonString = IOUtils.toString(
+ XmlParserHl7OrgDstu2Test.class.getResourceAsStream("/example-patient-general-hl7orgdstu2.xml"),
+ Charset.forName("UTF-8"));
+
+ String expected = (jsonString);
+ String actual = (encoded.trim());
+
+ compareXml(expected, actual);
+ }
+
+ @Test
+ public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
+ String refVal = "http://my.org/FooBar";
+
+ org.hl7.fhir.instance.model.Patient fhirPat = new org.hl7.fhir.instance.model.Patient();
+ fhirPat.addExtension().setUrl("x1").setValue(new org.hl7.fhir.instance.model.Reference(refVal));
+
+ IParser parser = ourCtx.newXmlParser();
+
+ String output = parser.encodeResourceToString(fhirPat);
+ System.out.println("output: " + output);
+
+ // Deserialize then check that valueReference value is still correct
+ fhirPat = parser.parseResource(org.hl7.fhir.instance.model.Patient.class, output);
+
+ List extlst = fhirPat.getExtension();
+ Assert.assertEquals(1, extlst.size());
+ Assert.assertEquals(refVal, ((org.hl7.fhir.instance.model.Reference) extlst.get(0).getValue()).getReference());
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class TestPatientFor327 extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "testCondition")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "testCondition", definedLocally = true, isModifier = false)
+ private List testConditions = null;
+
+ public List getConditions() {
+ return this.testConditions;
+ }
+
+ public void setCondition(List ref) {
+ this.testConditions = ref;
+ }
+ }
+
+ public static void compareXml(String content, String reEncoded) {
+ Diff d = DiffBuilder.compare(Input.fromString(content))
+ .withTest(Input.fromString(reEncoded))
+ .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .checkForSimilar()
+ .ignoreWhitespace()
+ .ignoreComments()
+ .withComparisonController(ComparisonControllers.Default)
+ .build();
+
+ assertTrue(d.toString(), !d.hasDifferences());
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class MyPatientWithOneDeclaredAddressExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(order = 0, name = "foo")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private Address myFoo;
+
+ public Address getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(Address theFoo) {
+ myFoo = theFoo;
+ }
+
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class MyPatientWithOneDeclaredExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(order = 0, name = "foo")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private Reference myFoo;
+
+ public Reference getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(Reference theFoo) {
+ myFoo = theFoo;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomDiagnosticReport.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomDiagnosticReport.java
new file mode 100644
index 00000000000..37edb7a955f
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomDiagnosticReport.java
@@ -0,0 +1,13 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.DiagnosticReport;
+
+@ResourceDef(name = "DiagnosticReport", profile = CustomDiagnosticReport.PROFILE)
+public class CustomDiagnosticReport extends DiagnosticReport {
+
+ public static final String PROFILE = "http://custom_DiagnosticReport";
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomObservation.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomObservation.java
new file mode 100644
index 00000000000..470ba8690da
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomObservation.java
@@ -0,0 +1,13 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.Observation;
+
+@ResourceDef(name = "Observation", profile = CustomObservation.PROFILE)
+public class CustomObservation extends Observation {
+
+ public static final String PROFILE = "http://custom_Observation";
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomPatientDstu3.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomPatientDstu3.java
new file mode 100644
index 00000000000..e4baa629e05
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/CustomPatientDstu3.java
@@ -0,0 +1,29 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.BooleanType;
+import org.hl7.fhir.dstu3.model.Patient;
+
+@ResourceDef(name = "Patient", profile = "Patient")
+public class CustomPatientDstu3 extends Patient {
+
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "homeless", order = 1)
+ @Extension(url = "/StructureDefinition/homeless", definedLocally = true, isModifier = false)
+ @Description(shortDefinition = "The patient being homeless, true if homeless")
+ private BooleanType homeless;
+
+
+ public BooleanType getHomeless() {
+ return homeless;
+ }
+
+ public void setHomeless(final BooleanType homeless) {
+ this.homeless = homeless;
+ }
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/Dstu3JsonParserTest.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/Dstu3JsonParserTest.java
new file mode 100644
index 00000000000..1e3ff368610
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/Dstu3JsonParserTest.java
@@ -0,0 +1,1700 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.parser.LenientErrorHandler;
+import ca.uhn.fhir.parser.StrictErrorHandler;
+import com.google.common.collect.Sets;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.hl7.fhir.dstu3.model.AuditEvent;
+import org.hl7.fhir.dstu3.model.Basic;
+import org.hl7.fhir.dstu3.model.Binary;
+import org.hl7.fhir.dstu3.model.Bundle;
+import org.hl7.fhir.dstu3.model.CapabilityStatement;
+import org.hl7.fhir.dstu3.model.Claim;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.Condition;
+import org.hl7.fhir.dstu3.model.Coverage;
+import org.hl7.fhir.dstu3.model.DateTimeType;
+import org.hl7.fhir.dstu3.model.DateType;
+import org.hl7.fhir.dstu3.model.DecimalType;
+import org.hl7.fhir.dstu3.model.DocumentManifest;
+import org.hl7.fhir.dstu3.model.Enumerations;
+import org.hl7.fhir.dstu3.model.ExplanationOfBenefit;
+import org.hl7.fhir.dstu3.model.Extension;
+import org.hl7.fhir.dstu3.model.HumanName;
+import org.hl7.fhir.dstu3.model.IdType;
+import org.hl7.fhir.dstu3.model.Identifier;
+import org.hl7.fhir.dstu3.model.Linkage;
+import org.hl7.fhir.dstu3.model.Observation;
+import org.hl7.fhir.dstu3.model.Parameters;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.Quantity;
+import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
+import org.hl7.fhir.dstu3.model.Reference;
+import org.hl7.fhir.dstu3.model.SampledData;
+import org.hl7.fhir.dstu3.model.SimpleQuantity;
+import org.hl7.fhir.dstu3.model.StringType;
+import org.hl7.fhir.dstu3.model.UriType;
+import org.hl7.fhir.dstu3.model.ValueSet;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_DSTU3;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.apache.commons.lang3.StringUtils.countMatches;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class Dstu3JsonParserTest {
+
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu3JsonParserTest.class);
+ private FhirContext ourCtx = FhirContext.forDstu3();
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_DSTU3.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ @Test
+ public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
+ String refVal = "http://my.org/FooBar";
+
+ Patient fhirPat = new Patient();
+ fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
+
+ IParser parser = ourCtx.newJsonParser();
+
+ String output = parser.encodeResourceToString(fhirPat);
+ System.out.println("output: " + output);
+
+ // Deserialize then check that valueReference value is still correct
+ fhirPat = parser.parseResource(Patient.class, output);
+
+ List extlst = fhirPat.getExtensionsByUrl("x1");
+ Assert.assertEquals(1, extlst.size());
+ Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
+ }
+
+ /**
+ * See #544
+ */
+ @Test
+ public void testBundleStitchReferencesByUuid() throws Exception {
+ Bundle bundle = new Bundle();
+
+ DocumentManifest dm = new DocumentManifest();
+ dm.getSubject().setReference("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3");
+ bundle.addEntry().setResource(dm);
+
+ Patient patient = new Patient();
+ patient.addName().setFamily("FAMILY");
+ bundle.addEntry().setResource(patient).setFullUrl("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3");
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle);
+ ourLog.info(encoded);
+
+ bundle = ourCtx.newJsonParser().parseResource(Bundle.class, encoded);
+ dm = (DocumentManifest) bundle.getEntry().get(0).getResource();
+
+ assertEquals("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3", dm.getSubject().getReference());
+
+ Patient subject = (Patient) dm.getSubject().getResource();
+ assertNotNull(subject);
+ assertEquals("FAMILY", subject.getNameFirstRep().getFamily());
+ }
+
+ /**
+ * Test for the url generated based on the server config
+ */
+ @Test
+ public void testCustomUrlExtension() {
+ final String expected = "{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}";
+
+ final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
+ patient.setPetName(new StringType("myName"));
+
+ final IParser jsonParser = ourCtx.newJsonParser();
+ jsonParser.setServerBaseUrl("http://www.example.com");
+
+ final String parsedPatient = jsonParser.encodeResourceToString(patient);
+ System.out.println(parsedPatient);
+ assertEquals(expected, parsedPatient);
+
+ // Parse with string
+ MyPatientWithCustomUrlExtension newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, parsedPatient);
+ assertEquals("myName", newPatient.getPetName().getValue());
+
+ // Parse with stream
+ newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
+ assertEquals("myName", newPatient.getPetName().getValue());
+
+ // Check no NPE if base server not configure
+ newPatient = ourCtx.newJsonParser().parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
+ assertNull("myName", newPatient.getPetName().getValue());
+ assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
+ }
+
+ @Test
+ public void testCustomUrlExtensioninBundle() {
+ final String expected = "{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}}]}";
+
+ final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
+ patient.setPetName(new StringType("myName"));
+
+ final Bundle bundle = new Bundle();
+ final Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
+ entry.setResource(patient);
+ bundle.addEntry(entry);
+
+ final IParser jsonParser = ourCtx.newJsonParser();
+ jsonParser.setServerBaseUrl("http://www.example.com");
+
+ final String parsedBundle = jsonParser.encodeResourceToString(bundle);
+ System.out.println(parsedBundle);
+ assertEquals(expected, parsedBundle);
+
+ // Parse with string
+ Bundle newBundle = jsonParser.parseResource(Bundle.class, parsedBundle);
+ assertNotNull(newBundle);
+ assertEquals(1, newBundle.getEntry().size());
+ Patient newPatient = (Patient) newBundle.getEntry().get(0).getResource();
+ assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
+
+ // Parse with stream
+ newBundle = jsonParser.parseResource(Bundle.class, new StringReader(parsedBundle));
+ assertNotNull(newBundle);
+ assertEquals(1, newBundle.getEntry().size());
+ newPatient = (Patient) newBundle.getEntry().get(0).getResource();
+ assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
+
+ }
+
+ /**
+ * See #276
+ */
+ @Test
+ public void testDoubleEncodingContainedResources() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("#patient-1");
+ patient.setActive(true);
+
+ Coverage coverage = new Coverage();
+ coverage.setId("#coverage-1");
+ coverage.getBeneficiary().setResource(patient);
+
+ Claim resource = new Claim();
+ resource.getContained().add(patient);
+ resource.getContained().add(coverage);
+ resource.getPatient().setReference("#patient-1");
+ resource.addInsurance().getCoverage().setReference("#coverage-1");
+
+ IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
+ String encoded = p.encodeResourceToString(resource);
+ ourLog.info(encoded);
+
+ assertEquals(3, countMatches(encoded, "resourceType"));
+ }
+
+ @Test
+ public void testEncodeAndParseExtensions() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setUse(Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ Extension ext = new Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
+ patient.addExtension(ext);
+
+ Extension parent = new Extension().setUrl("http://example.com#parent");
+ patient.addExtension(parent);
+ Extension child1 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.addExtension(child1);
+ Extension child2 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value2"));
+ parent.addExtension(child2);
+
+ Extension modExt = new Extension();
+ modExt.setUrl("http://example.com/extensions#modext");
+ modExt.setValue(new DateType("1995-01-02"));
+ patient.addModifierExtension(modExt);
+
+ HumanName name = patient.addName();
+ name.setFamily("Blah");
+ StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given"));
+ given.addExtension(ext2);
+
+ StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent");
+ given2.addExtension(given2ext);
+ given2ext.addExtension(new Extension().setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD")));
+
+ String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newJsonParser().encodeResourceToString(patient);
+ assertThat(enc, stringContainsInOrder("{\"resourceType\":\"Patient\",", "\"extension\":[{\"url\":\"http://example.com/extensions#someext\",\"valueDateTime\":\"2011-01-02T11:13:15\"}",
+ "{\"url\":\"http://example.com#parent\",\"extension\":[{\"url\":\"http://example.com#child\",\"valueString\":\"value1\"},{\"url\":\"http://example.com#child\",\"valueString\":\"value2\"}]}"));
+ assertThat(enc, stringContainsInOrder("\"modifierExtension\":[" + "{" + "\"url\":\"http://example.com/extensions#modext\"," + "\"valueDate\":\"1995-01-02\"" + "}" + "],"));
+ assertThat(enc,
+ containsString("\"_given\":[" + "{" + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext\"," + "\"valueString\":\"given\"" + "}" + "]" + "}," + "{" + "\"extension\":[" + "{"
+ + "\"url\":\"http://examples.com#givenext_parent\"," + "\"extension\":[" + "{" + "\"url\":\"http://examples.com#givenext_child\"," + "\"valueString\":\"CHILD\"" + "}" + "]" + "}"
+ + "]" + "}"));
+
+ /*
+ * Now parse this back
+ */
+
+ Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, enc);
+ ext = parsed.getExtension().get(0);
+ assertEquals("http://example.com/extensions#someext", ext.getUrl());
+ assertEquals("2011-01-02T11:13:15", ((DateTimeType) ext.getValue()).getValueAsString());
+
+ parent = patient.getExtension().get(1);
+ assertEquals("http://example.com#parent", parent.getUrl());
+ assertNull(parent.getValue());
+ child1 = parent.getExtension().get(0);
+ assertEquals("http://example.com#child", child1.getUrl());
+ assertEquals("value1", ((StringType) child1.getValue()).getValueAsString());
+ child2 = parent.getExtension().get(1);
+ assertEquals("http://example.com#child", child2.getUrl());
+ assertEquals("value2", ((StringType) child2.getValue()).getValueAsString());
+
+ modExt = parsed.getModifierExtension().get(0);
+ assertEquals("http://example.com/extensions#modext", modExt.getUrl());
+ assertEquals("1995-01-02", ((DateType) modExt.getValue()).getValueAsString());
+
+ name = parsed.getName().get(0);
+
+ ext2 = name.getGiven().get(0).getExtension().get(0);
+ assertEquals("http://examples.com#givenext", ext2.getUrl());
+ assertEquals("given", ((StringType) ext2.getValue()).getValueAsString());
+
+ given2ext = name.getGiven().get(1).getExtension().get(0);
+ assertEquals("http://examples.com#givenext_parent", given2ext.getUrl());
+ assertNull(given2ext.getValue());
+ Extension given2ext2 = given2ext.getExtension().get(0);
+ assertEquals("http://examples.com#givenext_child", given2ext2.getUrl());
+ assertEquals("CHILD", ((StringType) given2ext2.getValue()).getValue());
+
+ }
+
+ private Matcher super String> stringContainsInOrder(java.lang.String... substrings) {
+ return Matchers.stringContainsInOrder(Arrays.asList(substrings));
+ }
+
+ @Test
+ public void testEncodeAndParseMetaProfileAndTags() {
+ Patient p = new Patient();
+ p.addName().setFamily("FAMILY");
+
+ p.getMeta().addProfile("http://foo/Profile1");
+ p.getMeta().addProfile("http://foo/Profile2");
+
+ p.getMeta().addTag().setSystem("scheme1").setCode("term1").setDisplay("label1");
+ p.getMeta().addTag().setSystem("scheme2").setCode("term2").setDisplay("label2");
+
+ p.getMeta().addSecurity().setSystem("sec_scheme1").setCode("sec_term1").setDisplay("sec_label1");
+ p.getMeta().addSecurity().setSystem("sec_scheme2").setCode("sec_term2").setDisplay("sec_label2");
+
+ String enc = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+
+ //@formatter:off
+ assertThat(enc, stringContainsInOrder("\"meta\": {",
+ "\"profile\": [",
+ "\"http://foo/Profile1\",",
+ "\"http://foo/Profile2\"",
+ "],",
+ "\"security\": [",
+ "{",
+ "\"system\": \"sec_scheme1\",",
+ "\"code\": \"sec_term1\",",
+ "\"display\": \"sec_label1\"",
+ "},",
+ "{",
+ "\"system\": \"sec_scheme2\",",
+ "\"code\": \"sec_term2\",",
+ "\"display\": \"sec_label2\"",
+ "}",
+ "],",
+ "\"tag\": [",
+ "{",
+ "\"system\": \"scheme1\",",
+ "\"code\": \"term1\",",
+ "\"display\": \"label1\"",
+ "},",
+ "{",
+ "\"system\": \"scheme2\",",
+ "\"code\": \"term2\",",
+ "\"display\": \"label2\"",
+ "}",
+ "]",
+ "},"));
+ //@formatter:on
+
+ Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, enc);
+
+ List gotLabels = parsed.getMeta().getProfile();
+ assertEquals(2, gotLabels.size());
+ UriType label = gotLabels.get(0);
+ assertEquals("http://foo/Profile1", label.getValue());
+ label = gotLabels.get(1);
+ assertEquals("http://foo/Profile2", label.getValue());
+
+ List tagList = parsed.getMeta().getTag();
+ assertEquals(2, tagList.size());
+ assertEquals("scheme1", tagList.get(0).getSystem());
+ assertEquals("term1", tagList.get(0).getCode());
+ assertEquals("label1", tagList.get(0).getDisplay());
+ assertEquals("scheme2", tagList.get(1).getSystem());
+ assertEquals("term2", tagList.get(1).getCode());
+ assertEquals("label2", tagList.get(1).getDisplay());
+
+ tagList = parsed.getMeta().getSecurity();
+ assertEquals(2, tagList.size());
+ assertEquals("sec_scheme1", tagList.get(0).getSystem());
+ assertEquals("sec_term1", tagList.get(0).getCode());
+ assertEquals("sec_label1", tagList.get(0).getDisplay());
+ assertEquals("sec_scheme2", tagList.get(1).getSystem());
+ assertEquals("sec_term2", tagList.get(1).getCode());
+ assertEquals("sec_label2", tagList.get(1).getDisplay());
+ }
+
+ /**
+ * See #336
+ */
+ @Test
+ public void testEncodeAndParseNullPrimitiveWithExtensions() {
+
+ Patient p = new Patient();
+ p.setId("patid");
+ HumanName name = p.addName();
+ name.addGivenElement().setValue(null).setId("f0").addExtension(new Extension("http://foo", new StringType("FOOEXT0")));
+ name.addGivenElement().setValue("V1").setId("f1").addExtension((Extension) new Extension("http://foo", new StringType("FOOEXT1")).setId("ext1id"));
+ name.addGivenElement(); // this one shouldn't get encoded
+ name.addGivenElement().setValue(null).addExtension(new Extension("http://foo", new StringType("FOOEXT3")));
+ name.setId("nameid");
+
+ String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(output);
+
+ output = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(p);
+ String expected = "{\"resourceType\":\"Patient\",\"id\":\"patid\",\"name\":[{\"id\":\"nameid\",\"given\":[null,\"V1\",null],\"_given\":[{\"id\":\"f0\",\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"FOOEXT0\"}]},{\"id\":\"f1\",\"extension\":[{\"id\":\"ext1id\",\"url\":\"http://foo\",\"valueString\":\"FOOEXT1\"}]},{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"FOOEXT3\"}]}]}]}";
+ assertEquals(expected, output);
+
+ p = ourCtx.newJsonParser().parseResource(Patient.class, output);
+ assertEquals("patid", p.getIdElement().getIdPart());
+
+ name = p.getName().get(0);
+ assertEquals("nameid", name.getId());
+ assertEquals(3, name.getGiven().size());
+
+ assertEquals(null, name.getGiven().get(0).getValue());
+ assertEquals("V1", name.getGiven().get(1).getValue());
+ assertEquals(null, name.getGiven().get(2).getValue());
+
+ assertEquals("f0", name.getGiven().get(0).getId());
+ assertEquals("f1", name.getGiven().get(1).getId());
+ assertEquals(null, name.getGiven().get(2).getId());
+
+ assertEquals(1, name.getGiven().get(0).getExtension().size());
+ assertEquals("http://foo", name.getGiven().get(0).getExtension().get(0).getUrl());
+ assertEquals("FOOEXT0", ((StringType) name.getGiven().get(0).getExtension().get(0).getValue()).getValue());
+ assertEquals(null, name.getGiven().get(0).getExtension().get(0).getId());
+
+ assertEquals(1, name.getGiven().get(1).getExtension().size());
+ assertEquals("http://foo", name.getGiven().get(1).getExtension().get(0).getUrl());
+ assertEquals("FOOEXT1", ((StringType) name.getGiven().get(1).getExtension().get(0).getValue()).getValue());
+ assertEquals("ext1id", name.getGiven().get(1).getExtension().get(0).getId());
+
+ assertEquals(1, name.getGiven().get(2).getExtension().size());
+ assertEquals("http://foo", name.getGiven().get(2).getExtension().get(0).getUrl());
+ assertEquals("FOOEXT3", ((StringType) name.getGiven().get(2).getExtension().get(0).getValue()).getValue());
+ assertEquals(null, name.getGiven().get(2).getExtension().get(0).getId());
+
+ }
+
+ @Test
+ public void testEncodeAndParseSecurityLabels() {
+ Patient p = new Patient();
+ p.addName().setFamily("FAMILY");
+
+ List labels = new ArrayList();
+ labels.add(new Coding().setSystem("SYSTEM1").setCode("CODE1").setDisplay("DISPLAY1").setVersion("VERSION1"));
+ labels.add(new Coding().setSystem("SYSTEM2").setCode("CODE2").setDisplay("DISPLAY2").setVersion("VERSION2"));
+ p.getMeta().getSecurity().addAll(labels);
+
+ String enc = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+
+ //@formatter:off
+ assertEquals("{\n" +
+ " \"resourceType\": \"Patient\",\n" +
+ " \"meta\": {\n" +
+ " \"security\": [\n" +
+ " {\n" +
+ " \"system\": \"SYSTEM1\",\n" +
+ " \"version\": \"VERSION1\",\n" +
+ " \"code\": \"CODE1\",\n" +
+ " \"display\": \"DISPLAY1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"system\": \"SYSTEM2\",\n" +
+ " \"version\": \"VERSION2\",\n" +
+ " \"code\": \"CODE2\",\n" +
+ " \"display\": \"DISPLAY2\"\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " \"name\": [\n" +
+ " {\n" +
+ " \"family\": \"FAMILY\"\n" +
+ " }\n" +
+ " ]\n" +
+ "}", enc.trim());
+ //@formatter:on
+
+ Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, enc);
+ List gotLabels = parsed.getMeta().getSecurity();
+
+ assertEquals(2, gotLabels.size());
+
+ Coding label = gotLabels.get(0);
+ assertEquals("SYSTEM1", label.getSystem());
+ assertEquals("CODE1", label.getCode());
+ assertEquals("DISPLAY1", label.getDisplay());
+ assertEquals("VERSION1", label.getVersion());
+
+ label = gotLabels.get(1);
+ assertEquals("SYSTEM2", label.getSystem());
+ assertEquals("CODE2", label.getCode());
+ assertEquals("DISPLAY2", label.getDisplay());
+ assertEquals("VERSION2", label.getVersion());
+ }
+
+ @Test
+ public void testEncodeBinaryWithSecurityContext() {
+ Binary bin = new Binary();
+ bin.setContentType("text/plain");
+ bin.setContent("Now is the time".getBytes());
+ Reference securityContext = new Reference();
+ securityContext.setReference("DiagnosticReport/1");
+ bin.setSecurityContext(securityContext);
+ String encoded = ourCtx.newJsonParser().encodeResourceToString(bin);
+ ourLog.info(encoded);
+ assertThat(encoded, containsString("Binary"));
+ assertThat(encoded, containsString("\"contentType\":\"text/plain\""));
+ assertThat(encoded, containsString("\"content\":\"Tm93IGlzIHRoZSB0aW1l\""));
+ assertThat(encoded, containsString("\"securityContext\":{\"reference\":\"DiagnosticReport/1\"}"));
+ }
+
+ @Test
+ public void testEncodeBundleNewBundleNoText() {
+
+ Bundle b = new Bundle();
+
+ Bundle.BundleEntryComponent e = b.addEntry();
+ e.setResource(new Patient());
+
+ String val = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(b);
+ ourLog.info(val);
+ assertThat(val, not(containsString("text")));
+
+ val = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(b);
+ ourLog.info(val);
+ assertThat(val, not(containsString("text")));
+
+ }
+
+ /**
+ * See #326
+ */
+ @Test
+ public void testEncodeContainedResource() {
+ Patient patient = new Patient();
+ patient.getBirthDateElement().setValueAsString("2016-04-05");
+ patient.addExtension().setUrl("test").setValue(new Reference(new Condition()));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "{",
+ "\"resourceType\": \"Patient\",",
+ "\"contained\": [",
+ "{",
+ "\"resourceType\": \"Condition\",",
+ "\"id\": \"1\"",
+ "}",
+ "],",
+ "\"extension\": [",
+ "{",
+ "\"url\": \"test\",",
+ "\"valueReference\": {",
+ "\"reference\": \"#1\"",
+ "}",
+ "}",
+ "],",
+ "\"birthDate\": \"2016-04-05\"",
+ "}"
+ ));
+ //@formatter:on
+ }
+
+ @Test
+ public void testEncodeDoesntIncludeUuidId() {
+ Patient p = new Patient();
+ p.setId(new IdType("urn:uuid:42795ed8-041f-4ebf-b6f4-78ef6f64c2f2"));
+ p.addIdentifier().setSystem("ACME");
+
+ String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+ assertThat(actual, not(containsString("78ef6f64c2f2")));
+ }
+
+ @Test
+ public void testEncodeEmptyBinary() {
+ String output = ourCtx.newJsonParser().encodeResourceToString(new Binary());
+ assertEquals("{\"resourceType\":\"Binary\"}", output);
+ }
+
+ /**
+ * #158
+ */
+ @Test
+ public void testEncodeEmptyTag() {
+ ArrayList tagList = new ArrayList();
+ tagList.add(new Coding());
+ tagList.add(new Coding().setDisplay("Label"));
+
+ Patient p = new Patient();
+ p.getMeta().getTag().addAll(tagList);
+
+ String encoded = ourCtx.newJsonParser().encodeResourceToString(p);
+ assertThat(encoded, not(containsString("tag")));
+ }
+
+ /**
+ * #158
+ */
+ @Test
+ public void testEncodeEmptyTag2() {
+ ArrayList tagList = new ArrayList();
+ tagList.add(new Coding().setSystem("scheme").setCode("code"));
+ tagList.add(new Coding().setDisplay("Label"));
+
+ Patient p = new Patient();
+ p.getMeta().getTag().addAll(tagList);
+
+ String encoded = ourCtx.newJsonParser().encodeResourceToString(p);
+ assertThat(encoded, containsString("tag"));
+ assertThat(encoded, containsString("scheme"));
+ assertThat(encoded, not(containsString("Label")));
+ }
+
+ /**
+ * #480
+ */
+ @Test
+ public void testEncodeEmptyValue() {
+ QuestionnaireResponse qr = new QuestionnaireResponse();
+ qr.setId("123");
+ qr.getAuthoredElement().setValueAsString("");
+ qr.getItemFirstRep().setLinkIdElement(new StringType());
+ qr.getItemFirstRep().addItem().setLinkIdElement(new StringType(""));
+ qr.getItemFirstRep().addItem().setLinkIdElement(new StringType("LINKID"));
+
+ String encoded = ourCtx.newJsonParser().encodeResourceToString(qr);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder("123"));
+ assertThat(encoded, not(stringContainsInOrder("\"\"")));
+ assertThat(encoded, not(stringContainsInOrder("null")));
+ }
+
+ @Test
+ public void testEncodeExtensionInPrimitiveElement() {
+
+ CapabilityStatement c = new CapabilityStatement();
+ c.getAcceptUnknownElement().addExtension().setUrl("http://foo").setValue(new StringType("AAA"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c);
+ ourLog.info(encoded);
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
+ ourLog.info(encoded);
+ assertEquals(encoded, "{\"resourceType\":\"CapabilityStatement\",\"_acceptUnknown\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}");
+
+ // Now with a value
+ ourLog.info("---------------");
+
+ c = new CapabilityStatement();
+ c.getAcceptUnknownElement().setValue(CapabilityStatement.UnknownContentCode.ELEMENTS);
+ c.getAcceptUnknownElement().addExtension().setUrl("http://foo").setValue(new StringType("AAA"));
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c);
+ ourLog.info(encoded);
+
+ encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
+ ourLog.info(encoded);
+ assertEquals(encoded, "{\"resourceType\":\"CapabilityStatement\",\"acceptUnknown\":\"elements\",\"_acceptUnknown\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}");
+
+ }
+
+ @Test
+ public void testEncodeExtensionOnRoot() {
+ Patient p = new Patient();
+ p.setId("Patient/B");
+ p
+ .addExtension()
+ .setUrl("http://foo")
+ .setValue(new Reference("Practitioner/A"));
+
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+ parser.setDontEncodeElements(new HashSet(Arrays.asList("*.id", "*.meta")));
+
+ String encoded = parser.encodeResourceToString(p);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("http://foo"));
+ assertThat(encoded, containsString("Practitioner/A"));
+ }
+
+ @Test
+ public void testEncodeExtensionUndeclaredNonModifier() {
+ Observation obs = new Observation();
+ obs.setId("1");
+ obs.getMeta().addProfile("http://profile");
+ obs.setStatus(Observation.ObservationStatus.FINAL);
+ Extension ext = obs.addExtension();
+ ext.setUrl("http://exturl").setValue(new StringType("ext_url_value"));
+
+ obs.getCode().setText("CODE");
+
+ IParser parser = ourCtx.newJsonParser();
+
+ String output = parser.setPrettyPrint(true).encodeResourceToString(obs);
+ ourLog.info(output);
+
+ //@formatter:off
+ assertThat(output, stringContainsInOrder(
+ "\"id\": \"1\"",
+ "\"meta\"",
+ "\"extension\"",
+ "\"url\": \"http://exturl\"",
+ "\"valueString\": \"ext_url_value\"",
+ "\"code\":"
+ ));
+ assertThat(output, not(stringContainsInOrder(
+ "\"url\": \"http://exturl\"",
+ ",",
+ "\"url\": \"http://exturl\""
+ )));
+ //@formatter:on
+
+ obs = parser.parseResource(Observation.class, output);
+ assertEquals(1, obs.getExtension().size());
+ assertEquals("http://exturl", obs.getExtension().get(0).getUrl());
+ assertEquals("ext_url_value", ((StringType) obs.getExtension().get(0).getValue()).getValue());
+ assertEquals("final", obs.getStatusElement().getValueAsString());
+ assertEquals(Observation.ObservationStatus.FINAL, obs.getStatusElement().getValue());
+
+ }
+
+ @Test
+ public void testEncodeExtensionUndeclaredNonModifierWithChildExtension() {
+ Observation obs = new Observation();
+ obs.setId("1");
+ obs.getMeta().addProfile("http://profile");
+ Extension ext = obs.addExtension();
+ ext.setUrl("http://exturl");
+
+ Extension subExt = ext.addExtension();
+ subExt.setUrl("http://subext").setValue(new StringType("sub_ext_value"));
+
+ obs.getCode().setText("CODE");
+
+ IParser parser = ourCtx.newJsonParser();
+
+ String output = parser.setPrettyPrint(true).encodeResourceToString(obs);
+ ourLog.info(output);
+
+ //@formatter:off
+ assertThat(output, stringContainsInOrder(
+ "\"id\": \"1\"",
+ "\"meta\"",
+ "\"extension\"",
+ "\"url\": \"http://exturl\"",
+ "\"extension\"",
+ "\"url\": \"http://subext\"",
+ "\"valueString\": \"sub_ext_value\"",
+ "\"code\":"
+ ));
+ assertThat(output, not(stringContainsInOrder(
+ "\"url\": \"http://exturl\"",
+ ",",
+ "\"url\": \"http://exturl\""
+ )));
+ //@formatter:on
+
+ obs = parser.parseResource(Observation.class, output);
+ assertEquals(1, obs.getExtension().size());
+ assertEquals("http://exturl", obs.getExtension().get(0).getUrl());
+ assertEquals(1, obs.getExtension().get(0).getExtension().size());
+ assertEquals("http://subext", obs.getExtension().get(0).getExtension().get(0).getUrl());
+ assertEquals("sub_ext_value", ((StringType) obs.getExtension().get(0).getExtension().get(0).getValue()).getValue());
+ }
+
+
+ @Test
+ public void testEncodeHistoryEncodeVersionsAtPath1() {
+ ourCtx = FhirContext.forDstu3();
+
+ assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
+
+ Patient p = new Patient();
+ p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
+
+ IParser parser = ourCtx.newJsonParser();
+
+ parser.setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization");
+ String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+ }
+
+ @Test
+ public void testEncodeHistoryEncodeVersionsAtPath2() {
+ ourCtx = FhirContext.forDstu3();
+
+ assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
+ assertTrue(ourCtx.getParserOptions().isStripVersionsFromReferences());
+
+ Patient p = new Patient();
+ p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
+
+ IParser parser = ourCtx.newJsonParser();
+
+ parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
+ String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+ }
+
+ @Test
+ public void testEncodeHistoryEncodeVersionsAtPath3() {
+ ourCtx = FhirContext.forDstu3();
+
+ assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
+
+ AuditEvent auditEvent = new AuditEvent();
+ auditEvent.addEntity().setReference(new Reference("http://foo.com/Organization/2/_history/1"));
+
+ IParser parser = ourCtx.newJsonParser();
+
+ parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference");
+ String enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+
+ parser.setDontStripVersionsFromReferencesAtPaths(new ArrayList());
+ enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+
+ parser.setDontStripVersionsFromReferencesAtPaths((String[]) null);
+ enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+
+ parser.setDontStripVersionsFromReferencesAtPaths((List) null);
+ enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+ }
+
+ @Test
+ public void testEncodeHistoryEncodeVersionsAtPathUsingOptions() {
+ ourCtx = FhirContext.forDstu3();
+
+ assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
+ assertTrue(ourCtx.getParserOptions().isStripVersionsFromReferences());
+ assertThat(ourCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths(), empty());
+
+ Patient p = new Patient();
+ p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
+
+ IParser parser = ourCtx.newJsonParser();
+
+ ourCtx.getParserOptions().setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization");
+ String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+
+ ourCtx.getParserOptions().setDontStripVersionsFromReferencesAtPaths(Arrays.asList("Patient.managingOrganization"));
+ enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+
+ ourCtx.getParserOptions().setDontStripVersionsFromReferencesAtPaths(new HashSet(Arrays.asList("Patient.managingOrganization")));
+ enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+ }
+
+ @Test
+ public void testEncodeHistoryStripVersionsFromReferences() {
+ ourCtx = FhirContext.forDstu3();
+
+ assertNull(ourCtx.newJsonParser().getStripVersionsFromReferences());
+
+ Patient p = new Patient();
+ p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
+
+ IParser parser = ourCtx.newJsonParser();
+ String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+
+ parser.setStripVersionsFromReferences(false);
+ enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+
+ ourCtx = FhirContext.forDstu3();
+ }
+
+ @Test
+ public void testEncodeHistoryStripVersionsFromReferencesFromContext() {
+ ourCtx = FhirContext.forDstu3();
+
+ assertTrue(ourCtx.getParserOptions().isStripVersionsFromReferences());
+
+ Patient p = new Patient();
+ p.setManagingOrganization(new Reference("http://foo.com/Organization/2/_history/1"));
+
+ IParser parser = ourCtx.newJsonParser();
+ String enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+
+ ourCtx.getParserOptions().setStripVersionsFromReferences(false);
+ enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2/_history/1\""));
+
+ parser.setStripVersionsFromReferences(true);
+ enc = parser.setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+ assertThat(enc, containsString("\"reference\": \"http://foo.com/Organization/2\""));
+
+ ourCtx = FhirContext.forDstu3();
+ }
+
+ @Test
+ public void testEncodeNarrativeShouldIncludeNamespace() {
+
+ Patient p = new Patient();
+ p.getText().setDivAsString("VALUE");
+
+ String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(output);
+ assertThat(output, containsString("\"div\": \"VALUE\""));
+ }
+
+ @Test
+ public void testEncodeNarrativeShouldIncludeNamespaceWithProcessingInstruction() {
+
+ Patient p = new Patient();
+ p.getText().setDivAsString("VALUE");
+
+ String output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(output);
+ assertThat(output, containsString("\"div\": \"VALUE\""));
+ }
+
+ @Test
+ public void testEncodeNarrativeSuppressed() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().setFamily("FAMILY");
+ patient.getMaritalStatus().addCoding().setCode("D");
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSuppressNarratives(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Patient"));
+ assertThat(encoded, stringContainsInOrder(ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM, ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE));
+ assertThat(encoded, not(containsString("text")));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, containsString("maritalStatus"));
+ }
+
+ @Test
+ public void testEncodeParametersWithId() {
+ Parameters reqParms = new Parameters();
+ IdType patient = new IdType(1);
+ reqParms.addParameter().setName("patient").setValue(patient);
+
+ String enc = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(reqParms);
+ ourLog.info(enc);
+
+ assertThat(enc, containsString("\"valueId\": \"1\""));
+ }
+
+ @Test
+ public void testEncodeSummary() {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().setFamily("FAMILY");
+ patient.addPhoto().setTitle("green");
+ patient.getMaritalStatus().addCoding().setCode("D");
+
+ ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Patient"));
+ assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\",", "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeSummary2() {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("THE DIV");
+ patient.addName().setFamily("FAMILY");
+ patient.getMaritalStatus().addCoding().setCode("D");
+
+ patient.getMeta().addTag().setSystem("foo").setCode("bar");
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).setSummaryMode(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Patient"));
+ assertThat(encoded, stringContainsInOrder("\"tag\"", "\"system\": \"foo\",", "\"code\": \"bar\"", "\"system\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_SYSTEM + "\"",
+ "\"code\": \"" + ca.uhn.fhir.rest.api.Constants.TAG_SUBSETTED_CODE + "\""));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ /**
+ * See #205
+ */
+ @Test
+ public void testEncodeTags() {
+ Patient pt = new Patient();
+ pt.addIdentifier().setSystem("sys").setValue("val");
+
+ pt.getMeta().addTag().setSystem("scheme").setCode("term").setDisplay("display");
+
+ String enc = ourCtx.newJsonParser().encodeResourceToString(pt);
+ ourLog.info(enc);
+
+ assertEquals("{\"resourceType\":\"Patient\",\"meta\":{\"tag\":[{\"system\":\"scheme\",\"code\":\"term\",\"display\":\"display\"}]},\"identifier\":[{\"system\":\"sys\",\"value\":\"val\"}]}",
+ enc);
+
+ }
+
+ /**
+ * See #241
+ */
+ @Test
+ public void testEncodeThenParseShouldNotAddSpuriousId() throws Exception {
+ Condition condition = new Condition().setVerificationStatus(Condition.ConditionVerificationStatus.CONFIRMED);
+ Bundle bundle = new Bundle();
+ Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
+ entry.setId("123");
+ entry.setResource(condition);
+ bundle.getEntry().add(entry);
+ IParser parser = ourCtx.newJsonParser();
+ String json = parser.encodeResourceToString(bundle);
+ ourLog.info(json);
+ bundle = (Bundle) parser.parseResource(json);
+
+ assertEquals("123", bundle.getEntry().get(0).getId());
+
+ condition = (Condition) bundle.getEntry().get(0).getResource();
+ assertEquals(null, condition.getId());
+ }
+
+ @Test
+ public void testEncodeWithDontEncodeElements() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("123");
+
+ patient.getMeta().addProfile(("http://profile"));
+ patient.addName().setFamily("FAMILY").addGiven("GIVEN");
+ patient.addAddress().addLine("LINE1");
+
+ {
+ IParser p = ourCtx.newJsonParser();
+ p.setDontEncodeElements(Sets.newHashSet("*.meta", "*.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newJsonParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.meta", "Patient.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newJsonParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.name.family"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("GIVEN"));
+ assertThat(out, not(containsString("FAMILY")));
+ }
+ {
+ IParser p = ourCtx.newJsonParser();
+ p.setDontEncodeElements(Sets.newHashSet("*.meta", "*.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newJsonParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.meta"));
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient.name")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("id"));
+ assertThat(out, not(containsString("address")));
+ assertThat(out, not(containsString("meta")));
+ }
+ }
+
+ @Test
+ public void testEncodingNullExtension() {
+ Patient p = new Patient();
+ Extension extension = new Extension("http://foo#bar");
+ p.addExtension(extension);
+ String str = ourCtx.newJsonParser().encodeResourceToString(p);
+
+ assertEquals("{\"resourceType\":\"Patient\"}", str);
+
+ extension.setValue(new StringType());
+
+ str = ourCtx.newJsonParser().encodeResourceToString(p);
+ assertEquals("{\"resourceType\":\"Patient\"}", str);
+
+ extension.setValue(new StringType(""));
+
+ str = ourCtx.newJsonParser().encodeResourceToString(p);
+ assertEquals("{\"resourceType\":\"Patient\"}", str);
+
+ }
+
+ /**
+ * See #341
+ */
+ @Test
+ public void testExplanationOfBenefit() {
+ //@formatter:off
+ String input = "{" +
+ " \"resourceType\": \"ExplanationOfBenefit\"," +
+ " \"insurance\": {\n" +
+ " \"coverage\": {\n" +
+ " \"reference\": \"Coverage/123\"\n" +
+ " }\n" +
+ " },\n" +
+ " \"relationship\": {\n" +
+ " \"system\": \"http://hl7.org/fhir/relationship\",\n" +
+ " \"code\": \"1\",\n" +
+ " \"display\": \"self\"\n" +
+ " }\n" +
+ "}";
+ //@formatter:on
+
+ ExplanationOfBenefit eob = ourCtx.newJsonParser().parseResource(ExplanationOfBenefit.class, input);
+ assertEquals(Reference.class, eob.getInsurance().getCoverage().getClass());
+
+ Reference coverage = eob.getInsurance().getCoverage();
+ assertEquals("Coverage/123", coverage.getReference());
+ }
+
+ @Test
+ public void testExponentDoesntGetEncodedAsSuch() {
+ Observation obs = new Observation();
+ obs.setValue(new Quantity().setValue(new BigDecimal("0.000000000000000100")));
+
+ String str = ourCtx.newJsonParser().encodeResourceToString(obs);
+ ourLog.info(str);
+
+ assertEquals("{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.000000000000000100}}", str);
+ }
+
+ @Test
+ public void testExponentParseWorks() {
+ String input = "{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.0000000000000001}}";
+ Observation obs = ourCtx.newJsonParser().parseResource(Observation.class, input);
+
+ assertEquals("0.0000000000000001", ((Quantity) obs.getValue()).getValueElement().getValueAsString());
+
+ String str = ourCtx.newJsonParser().encodeResourceToString(obs);
+ ourLog.info(str);
+ assertEquals("{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.0000000000000001}}", str);
+ }
+
+ /**
+ * #516
+ */
+ @Test(expected = DataFormatException.class)
+ public void testInvalidEnumValue() {
+ String res = "{ \"resourceType\": \"ValueSet\", \"url\": \"http://sample/ValueSet/education-levels\", \"version\": \"1\", \"name\": \"Education Levels\", \"status\": \"draft\", \"compose\": { \"include\": [ { \"filter\": [ { \"property\": \"n\", \"op\": \"n\", \"value\": \"365460000\" } ], \"system\": \"http://snomed.info/sct\" } ], \"exclude\": [ { \"concept\": [ { \"code\": \"224298008\" }, { \"code\": \"365460000\" }, { \"code\": \"473462005\" }, { \"code\": \"424587006\" } ], \"system\": \"http://snomed.info/sct\" } ] }, \"description\": \"A selection of Education Levels\", \"text\": { \"status\": \"generated\", \"div\": \"Education Levels
http://csiro.au/ValueSet/education-levelsA selection of Education Levels
\" }, \"experimental\": true, \"date\": \"2016-07-26\" }";
+ IParser parser = ourCtx.newJsonParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ ValueSet parsed = parser.parseResource(ValueSet.class, res);
+ fail("DataFormat Invalid attribute exception should be thrown");
+ }
+
+ /**
+ * #65
+ */
+ @Test
+ public void testJsonPrimitiveWithExtensionEncoding() {
+
+ QuestionnaireResponse parsed = new QuestionnaireResponse();
+ parsed.addItem().setLinkId("value123");
+ parsed.getItem().get(0).getLinkIdElement().addExtension(new Extension("http://123", new StringType("HELLO")));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(parsed);
+ ourLog.info(encoded);
+ assertThat(encoded, containsString("{\"linkId\":\"value123\",\"_linkId\":{\"extension\":[{\"url\":\"http://123\",\"valueString\":\"HELLO\"}]}}"));
+
+ }
+
+ @Test
+ public void testLinkage() {
+ Linkage l = new Linkage();
+ l.addItem().getResource().setDisplay("FOO");
+ String out = ourCtx.newXmlParser().encodeResourceToString(l);
+ ourLog.info(out);
+ assertEquals(" ", out);
+ }
+
+
+ @Test
+ public void testOmitResourceId() {
+ Patient p = new Patient();
+ p.setId("123");
+ p.addName().setFamily("ABC");
+
+ assertThat(ourCtx.newJsonParser().encodeResourceToString(p), stringContainsInOrder("123", "ABC"));
+ assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), containsString("ABC"));
+ assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnFhirContext() {
+ try {
+ String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
+ ourCtx.getParserOptions().setOverrideResourceIdWithBundleEntryFullUrl(false);
+ Bundle bundle = (Bundle) ourCtx.newJsonParser().parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = FhirContext.forDstu3();
+ }
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnParser() {
+ try {
+ String tmp = "{\"resourceType\":\"Bundle\",\"entry\":[{\"fullUrl\":\"http://lalaland.org/patient/pat1\",\"resource\":{\"resourceType\":\"Patient\",\"id\":\"patxuzos\"}}]}";
+ Bundle bundle = (Bundle) ourCtx.newJsonParser().setOverrideResourceIdWithBundleEntryFullUrl(false).parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = FhirContext.forDstu3();
+ }
+ }
+
+
+ @Test
+ public void testParseAndEncodeBundleWithUuidBase() {
+ //@formatter:off
+ String input =
+ "{\n" +
+ " \"resourceType\":\"Bundle\",\n" +
+ " \"type\":\"document\",\n" +
+ " \"entry\":[\n" +
+ " {\n" +
+ " \"fullUrl\":\"urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57\",\n" +
+ " \"resource\":{\n" +
+ " \"resourceType\":\"Composition\",\n" +
+ " \"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\",\n" +
+ " \"meta\":{\n" +
+ " \"lastUpdated\":\"2013-05-28T22:12:21Z\"\n" +
+ " },\n" +
+ " \"text\":{\n" +
+ " \"status\":\"generated\",\n" +
+ " \"div\":\"Generated Narrative with Details
id: 180f219f-97a8-486d-99d9-ed631fe4fc57
meta:
date: Feb 1, 2013 12:30:02 PM
type: Discharge Summary from Responsible Clinician (Details : {LOINC code '28655-9' = 'Physician attending Discharge summary)
status: final
confidentiality: N
author: Doctor Dave. Generated Summary: 23; Adam Careful
encounter: http://fhir.healthintersections.com.au/open/Encounter/doc-example
\"\n" +
+ " },\n" +
+ " \"date\":\"2013-02-01T12:30:02Z\",\n" +
+ " \"type\":{\n" +
+ " \"coding\":[\n" +
+ " {\n" +
+ " \"system\":\"http://loinc.org\",\n" +
+ " \"code\":\"28655-9\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"text\":\"Discharge Summary from Responsible Clinician\"\n" +
+ " },\n" +
+ " \"status\":\"final\",\n" +
+ " \"confidentiality\":\"N\",\n" +
+ " \"subject\":{\n" +
+ " \"reference\":\"http://fhir.healthintersections.com.au/open/Patient/d1\",\n" +
+ " \"display\":\"Eve Everywoman\"\n" +
+ " },\n" +
+ " \"author\":[\n" +
+ " {\n" +
+ " \"reference\":\"Practitioner/example\",\n" +
+ " \"display\":\"Doctor Dave\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"encounter\":{\n" +
+ " \"reference\":\"http://fhir.healthintersections.com.au/open/Encounter/doc-example\"\n" +
+ " },\n" +
+ " \"section\":[\n" +
+ " {\n" +
+ " \"title\":\"Reason for admission\",\n" +
+ " \"content\":{\n" +
+ " \"reference\":\"urn:uuid:d0dd51d3-3ab2-4c84-b697-a630c3e40e7a\"\n" +
+ " }\n" +
+ " },\n" +
+ " {\n" +
+ " \"title\":\"Medications on Discharge\",\n" +
+ " \"content\":{\n" +
+ " \"reference\":\"urn:uuid:673f8db5-0ffd-4395-9657-6da00420bbc1\"\n" +
+ " }\n" +
+ " },\n" +
+ " {\n" +
+ " \"title\":\"Known allergies\",\n" +
+ " \"content\":{\n" +
+ " \"reference\":\"urn:uuid:68f86194-e6e1-4f65-b64a-5314256f8d7b\"\n" +
+ " }\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " }" +
+ " ]" +
+ "}";
+ //@formatter:on
+
+ Bundle parsed = ourCtx.newJsonParser().parseResource(Bundle.class, input);
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed);
+ ourLog.info(encoded);
+
+ assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+ assertEquals(null, parsed.getEntry().get(0).getResource().getIdElement().getBaseUrl());
+ assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", parsed.getEntry().get(0).getResource().getIdElement().getIdPart());
+ assertThat(encoded, not(containsString("\"id\":\"180f219f-97a8-486d-99d9-ed631fe4fc57\"")));
+ }
+
+ @Test
+ public void testParseAndEncodeComments() {
+ //@formatter:off
+ String input = "{\n" +
+ " \"resourceType\": \"Patient\",\n" +
+ " \"id\": \"pat1\",\n" +
+ " \"text\": {\n" +
+ " \"status\": \"generated\",\n" +
+ " \"div\": \"\\n \\n Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321
\\n \\n \"\n" +
+ " },\n" +
+ " \"identifier\": [\n" +
+ " {\n" +
+ " \"fhir_comments\":[\"identifier comment 1\",\"identifier comment 2\"],\n" +
+ " \"use\": \"usual\",\n" +
+ " \"_use\": {\n" +
+ " \"fhir_comments\":[\"use comment 1\",\"use comment 2\"]\n" +
+ " },\n" +
+ " \"type\": {\n" +
+ " \"coding\": [\n" +
+ " {\n" +
+ " \"system\": \"http://hl7.org/fhir/v2/0203\",\n" +
+ " \"code\": \"MR\"\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " \"system\": \"urn:oid:0.1.2.3.4.5.6.7\",\n" +
+ " \"value\": \"654321\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"active\": true" +
+ "}";
+ //@formatter:off
+
+ Patient res = ourCtx.newJsonParser().parseResource(Patient.class, input);
+ res.getFormatCommentsPre();
+ assertEquals("Patient/pat1", res.getId());
+ assertEquals("654321", res.getIdentifier().get(0).getValue());
+ assertEquals(true, res.getActive());
+
+ assertThat(res.getIdentifier().get(0).getFormatCommentsPre(), contains("identifier comment 1", "identifier comment 2"));
+ assertThat(res.getIdentifier().get(0).getUseElement().getFormatCommentsPre(), contains("use comment 1", "use comment 2"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(res);
+ ourLog.info(encoded);
+
+ //@formatter:off
+ assertThat(encoded, stringContainsInOrder(
+ "\"identifier\": [",
+ "{",
+ "\"fhir_comments\":",
+ "[",
+ "\"identifier comment 1\"",
+ ",",
+ "\"identifier comment 2\"",
+ "]",
+ "\"use\": \"usual\",",
+ "\"_use\": {",
+ "\"fhir_comments\":",
+ "[",
+ "\"use comment 1\"",
+ ",",
+ "\"use comment 2\"",
+ "]",
+ "},",
+ "\"type\""
+ ));
+ //@formatter:off
+ }
+
+ @Test
+ public void testParseBundleWithBinary() {
+ Binary patient = new Binary();
+ patient.setId(new IdType("http://base/Binary/11/_history/22"));
+ patient.setContentType("foo");
+ patient.setContent(new byte[]{1, 2, 3, 4});
+
+ String val = ourCtx.newJsonParser().encodeResourceToString(patient);
+
+ String expected = "{\"resourceType\":\"Binary\",\"id\":\"11\",\"meta\":{\"versionId\":\"22\"},\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}";
+ ourLog.info("Expected: {}", expected);
+ ourLog.info("Actual : {}", val);
+ assertEquals(expected, val);
+ }
+
+ /**
+ * #480
+ */
+ @Test
+ public void testParseEmptyValue() {
+ String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"group\":{\"linkId\":\"\"}}";
+ IParser parser = ourCtx.newJsonParser();
+
+ parser.setParserErrorHandler(new LenientErrorHandler().setErrorOnInvalidValue(false));
+ QuestionnaireResponse qr = parser.parseResource(QuestionnaireResponse.class, input);
+
+ assertEquals("QuestionnaireResponse/123", qr.getIdElement().getValue());
+ assertEquals(null, qr.getAuthored());
+ assertEquals(null, qr.getAuthoredElement().getValue());
+ assertEquals(null, qr.getAuthoredElement().getValueAsString());
+ assertEquals(null, qr.getItemFirstRep().getLinkId());
+ assertEquals(null, qr.getItemFirstRep().getLinkIdElement().getValue());
+ }
+
+ /**
+ * See #342
+ */
+ @Test()
+ public void testParseInvalid() {
+ try {
+ ourCtx.newJsonParser().parseResource("FOO");
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Failed to parse JSON content, error was: Content does not appear to be FHIR JSON, first non-whitespace character was: 'F' (must be '{')", e.getMessage());
+ }
+ try {
+ ourCtx.newJsonParser().parseResource("[\"aaa\"]");
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Failed to parse JSON content, error was: Content does not appear to be FHIR JSON, first non-whitespace character was: '[' (must be '{')", e.getMessage());
+ }
+
+ assertEquals(Bundle.class, ourCtx.newJsonParser().parseResource(" {\"resourceType\" : \"Bundle\"}").getClass());
+
+ }
+
+ /**
+ * See #414
+ */
+ @Test
+ public void testParseJsonExtensionWithoutUrl() {
+ //@formatter:off
+ String input =
+ "{\"resourceType\":\"Patient\"," +
+ "\"extension\":[ {\"valueDateTime\":\"2011-01-02T11:13:15\"} ]" +
+ "}";
+ //@formatter:on
+
+ IParser parser = ourCtx.newJsonParser();
+ parser.setParserErrorHandler(new LenientErrorHandler());
+ Patient parsed = (Patient) parser.parseResource(input);
+ assertEquals(1, parsed.getExtension().size());
+ assertEquals(null, parsed.getExtension().get(0).getUrl());
+ assertEquals("2011-01-02T11:13:15", parsed.getExtension().get(0).getValueAsPrimitive().getValueAsString());
+
+ try {
+ parser = ourCtx.newJsonParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ parser.parseResource(input);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getMessage());
+ }
+
+ }
+
+ /**
+ * See #414
+ */
+ @Test
+ public void testParseJsonModifierExtensionWithoutUrl() {
+ //@formatter:off
+ String input =
+ "{\"resourceType\":\"Patient\"," +
+ "\"modifierExtension\":[ {\"valueDateTime\":\"2011-01-02T11:13:15\"} ]" +
+ "}";
+ //@formatter:on
+
+ IParser parser = ourCtx.newJsonParser();
+ parser.setParserErrorHandler(new LenientErrorHandler());
+ Patient parsed = (Patient) parser.parseResource(input);
+ assertEquals(1, parsed.getModifierExtension().size());
+ assertEquals(null, parsed.getModifierExtension().get(0).getUrl());
+ assertEquals("2011-01-02T11:13:15", parsed.getModifierExtension().get(0).getValueAsPrimitive().getValueAsString());
+
+ try {
+ parser = ourCtx.newJsonParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ parser.parseResource(input);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getMessage());
+ }
+
+ }
+
+
+ /**
+ * See #484
+ */
+ @Test
+ public void testParseNarrativeWithEmptyDiv() {
+ String input = "{\"resourceType\":\"Basic\",\"id\":\"1\",\"text\":{\"status\":\"generated\",\"div\":\"\"}}";
+ Basic basic = ourCtx.newJsonParser().parseResource(Basic.class, input);
+ assertEquals(null, basic.getText().getDivAsString());
+
+ input = "{\"resourceType\":\"Basic\",\"id\":\"1\",\"text\":{\"status\":\"generated\",\"div\":\"\"}}";
+ basic = ourCtx.newJsonParser().parseResource(Basic.class, input);
+ assertEquals(null, basic.getText().getDivAsString());
+
+ input = "{\"resourceType\":\"Basic\",\"id\":\"1\",\"text\":{\"status\":\"generated\",\"div\":\" \"}}";
+ basic = ourCtx.newJsonParser().parseResource(Basic.class, input);
+ assertEquals(" ", basic.getText().getDivAsString());
+
+ }
+
+ /**
+ * See #163
+ */
+ @Test
+ public void testParseResourceType() {
+ IParser jsonParser = ourCtx.newJsonParser().setPrettyPrint(true);
+
+ // Patient
+ Patient patient = new Patient();
+ String patientId = UUID.randomUUID().toString();
+ patient.setId(new IdType("Patient", patientId));
+ patient.addName().addGiven("John").setFamily("Smith");
+ patient.setGender(Enumerations.AdministrativeGender.MALE);
+ patient.setBirthDateElement(new DateType("1987-04-16"));
+
+ // Bundle
+ Bundle bundle = new Bundle();
+ bundle.setType(Bundle.BundleType.COLLECTION);
+ bundle.addEntry().setResource(patient);
+
+ String bundleText = jsonParser.encodeResourceToString(bundle);
+ ourLog.info(bundleText);
+
+ Bundle reincarnatedBundle = jsonParser.parseResource(Bundle.class, bundleText);
+ Patient reincarnatedPatient = (Patient) reincarnatedBundle.getEntry().get(0).getResource();
+
+ assertEquals("Patient", patient.getIdElement().getResourceType());
+ assertEquals("Patient", reincarnatedPatient.getIdElement().getResourceType());
+ }
+
+ /**
+ * See #207
+ */
+ @Test
+ public void testParseResourceWithInvalidType() {
+ String input = "{" + "\"resourceType\":\"Patient\"," + "\"contained\":[" + " {" + " \"rezType\":\"Organization\"" + " }" + " ]" + "}";
+
+ IParser jsonParser = ourCtx.newJsonParser().setPrettyPrint(true);
+ try {
+ jsonParser.parseResource(input);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Missing required element 'resourceType' from JSON resource object, unable to parse", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testParseWithPrecision() {
+ String input = "{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.000000000000000100}}";
+ Observation obs = ourCtx.newJsonParser().parseResource(Observation.class, input);
+
+ DecimalType valueElement = ((Quantity) obs.getValue()).getValueElement();
+ assertEquals("0.000000000000000100", valueElement.getValueAsString());
+
+ String str = ourCtx.newJsonParser().encodeResourceToString(obs);
+ ourLog.info(str);
+ assertEquals("{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.000000000000000100}}", str);
+ }
+
+ @Test(expected = DataFormatException.class)
+ public void testParseWithTrailingContent() throws Exception {
+ //@formatter:off
+ String bundle = "{\n" +
+ " \"resourceType\" : \"Bundle\",\n" +
+ " \"total\" : 1\n" +
+ "}}";
+ //@formatter:on
+
+ Bundle b = ourCtx.newJsonParser().parseResource(Bundle.class, bundle);
+ }
+
+ /**
+ * See #344
+ */
+ @Test
+ public void testParserIsCaseSensitive() {
+ Observation obs = new Observation();
+ SampledData data = new SampledData();
+ data.setData("1 2 3");
+ data.setOrigin((SimpleQuantity) new SimpleQuantity().setValue(0L));
+ data.setPeriod(1000L);
+ obs.setValue(data);
+
+ IParser p = ourCtx.newJsonParser().setPrettyPrint(true).setParserErrorHandler(new StrictErrorHandler());
+ String encoded = p.encodeResourceToString(obs);
+ ourLog.info(encoded);
+
+ p.parseResource(encoded);
+
+ try {
+ p.parseResource(encoded.replace("Observation", "observation"));
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Unknown resource type 'observation': Resource names are case sensitive, found similar name: 'Observation'", e.getMessage());
+ }
+
+ try {
+ p.parseResource(encoded.replace("valueSampledData", "valueSampleddata"));
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Unknown element 'valueSampleddata' found during parse", e.getMessage());
+ }
+ }
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/Dstu3XmlParserTest.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/Dstu3XmlParserTest.java
new file mode 100644
index 00000000000..4f1194afe09
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/Dstu3XmlParserTest.java
@@ -0,0 +1,3235 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.parser.LenientErrorHandler;
+import ca.uhn.fhir.parser.StrictErrorHandler;
+import com.google.common.collect.Sets;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.hamcrest.core.StringContains;
+import org.hamcrest.text.StringContainsInOrder;
+import org.hl7.fhir.dstu3.model.Address;
+import org.hl7.fhir.dstu3.model.AllergyIntolerance;
+import org.hl7.fhir.dstu3.model.Annotation;
+import org.hl7.fhir.dstu3.model.Appointment;
+import org.hl7.fhir.dstu3.model.AuditEvent;
+import org.hl7.fhir.dstu3.model.Binary;
+import org.hl7.fhir.dstu3.model.BooleanType;
+import org.hl7.fhir.dstu3.model.Bundle;
+import org.hl7.fhir.dstu3.model.CodeType;
+import org.hl7.fhir.dstu3.model.CodeableConcept;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.Composition;
+import org.hl7.fhir.dstu3.model.ConceptMap;
+import org.hl7.fhir.dstu3.model.Condition;
+import org.hl7.fhir.dstu3.model.ContactPoint;
+import org.hl7.fhir.dstu3.model.DataElement;
+import org.hl7.fhir.dstu3.model.DateTimeType;
+import org.hl7.fhir.dstu3.model.DateType;
+import org.hl7.fhir.dstu3.model.DiagnosticReport;
+import org.hl7.fhir.dstu3.model.DocumentManifest;
+import org.hl7.fhir.dstu3.model.Duration;
+import org.hl7.fhir.dstu3.model.ElementDefinition;
+import org.hl7.fhir.dstu3.model.Encounter;
+import org.hl7.fhir.dstu3.model.EnumFactory;
+import org.hl7.fhir.dstu3.model.Enumeration;
+import org.hl7.fhir.dstu3.model.Enumerations;
+import org.hl7.fhir.dstu3.model.Extension;
+import org.hl7.fhir.dstu3.model.GuidanceResponse;
+import org.hl7.fhir.dstu3.model.HumanName;
+import org.hl7.fhir.dstu3.model.IdType;
+import org.hl7.fhir.dstu3.model.Identifier;
+import org.hl7.fhir.dstu3.model.InstantType;
+import org.hl7.fhir.dstu3.model.Location;
+import org.hl7.fhir.dstu3.model.Medication;
+import org.hl7.fhir.dstu3.model.MedicationRequest;
+import org.hl7.fhir.dstu3.model.MedicationStatement;
+import org.hl7.fhir.dstu3.model.Observation;
+import org.hl7.fhir.dstu3.model.Organization;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.Practitioner;
+import org.hl7.fhir.dstu3.model.PrimitiveType;
+import org.hl7.fhir.dstu3.model.ProcedureRequest;
+import org.hl7.fhir.dstu3.model.Quantity;
+import org.hl7.fhir.dstu3.model.Reference;
+import org.hl7.fhir.dstu3.model.Resource;
+import org.hl7.fhir.dstu3.model.SampledData;
+import org.hl7.fhir.dstu3.model.SimpleQuantity;
+import org.hl7.fhir.dstu3.model.StringType;
+import org.hl7.fhir.dstu3.model.UriType;
+import org.hl7.fhir.dstu3.model.ValueSet;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.xmlunit.builder.DiffBuilder;
+import org.xmlunit.builder.Input;
+import org.xmlunit.diff.ComparisonControllers;
+import org.xmlunit.diff.DefaultNodeMatcher;
+import org.xmlunit.diff.Diff;
+import org.xmlunit.diff.ElementSelectors;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_DSTU3;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class Dstu3XmlParserTest {
+
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu3XmlParserTest.class);
+ private FhirContext ourCtx = FhirContext.forDstu3();
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_DSTU3.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ mavenBundle().groupId("org.xmlunit").artifactId("xmlunit-core").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ @Test
+ public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
+ String refVal = "http://my.org/FooBar";
+
+ Patient fhirPat = new Patient();
+ fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
+
+ IParser parser = ourCtx.newXmlParser();
+
+ String output = parser.encodeResourceToString(fhirPat);
+ System.out.println("output: " + output);
+
+ // Deserialize then check that valueReference value is still correct
+ fhirPat = parser.parseResource(Patient.class, output);
+
+ List extlst = fhirPat.getExtensionsByUrl("x1");
+ Assert.assertEquals(1, extlst.size());
+ Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
+ }
+
+ /**
+ * See #544
+ */
+ @Test
+ public void testBundleStitchReferencesByUuid() throws Exception {
+ Bundle bundle = new Bundle();
+
+ DocumentManifest dm = new DocumentManifest();
+ dm.getSubject().setReference("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3");
+ bundle.addEntry().setResource(dm);
+
+ Patient patient = new Patient();
+ patient.addName().setFamily("FAMILY");
+ bundle.addEntry().setResource(patient).setFullUrl("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3");
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle);
+ ourLog.info(encoded);
+
+ bundle = ourCtx.newXmlParser().parseResource(Bundle.class, encoded);
+ dm = (DocumentManifest) bundle.getEntry().get(0).getResource();
+
+ assertEquals("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3", dm.getSubject().getReference());
+
+ Patient subject = (Patient) dm.getSubject().getResource();
+ assertNotNull(subject);
+ assertEquals("FAMILY", subject.getNameFirstRep().getFamily());
+ }
+
+ @Test
+ public void testBundleWithBinary() {
+
+ String bundle = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Bundle b = ourCtx.newXmlParser().parseResource(Bundle.class, bundle);
+ assertEquals(1, b.getEntry().size());
+
+ Binary bin = (Binary) b.getEntry().get(0).getResource();
+ assertArrayEquals(new byte[]{1, 2, 3, 4}, bin.getContent());
+
+ }
+
+
+ @Test
+ public void testContainedResourceInExtensionUndeclared() {
+ Patient p = new Patient();
+ p.addName().setFamily("PATIENT");
+
+ Organization o = new Organization();
+ o.setName("ORG");
+ p.addExtension(new Extension("urn:foo", new Reference(o)));
+
+ String str = ourCtx.newXmlParser().encodeResourceToString(p);
+ ourLog.info(str);
+
+ p = ourCtx.newXmlParser().parseResource(Patient.class, str);
+ assertEquals("PATIENT", p.getName().get(0).getFamily());
+
+ List exts = p.getExtensionsByUrl("urn:foo");
+ assertEquals(1, exts.size());
+ Reference rr = (Reference) exts.get(0).getValue();
+ o = (Organization) rr.getResource();
+ assertEquals("ORG", o.getName());
+ }
+
+ @Test(expected = DataFormatException.class)
+ public void testContainedResourceWithNoId() throws IOException {
+ String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ parser.parseResource(Bundle.class, string);
+ }
+
+ @Test()
+ public void testContainedResourceWithNoIdLenient() throws IOException {
+ String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new LenientErrorHandler());
+ parser.parseResource(Bundle.class, string);
+ }
+
+ /**
+ * Test for the url generated based on the server config
+ */
+ @Test
+ public void testCustomUrlExtension() {
+ final String expected = " ";
+
+ final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
+ patient.setPetName(new StringType("myName"));
+
+ final IParser xmlParser = ourCtx.newXmlParser();
+ xmlParser.setServerBaseUrl("http://www.example.com");
+
+ final String parsedPatient = xmlParser.encodeResourceToString(patient);
+ System.out.println(parsedPatient);
+ assertEquals(expected, parsedPatient);
+
+ // Parse with string
+ MyPatientWithCustomUrlExtension newPatient = xmlParser.parseResource(MyPatientWithCustomUrlExtension.class, parsedPatient);
+ assertEquals("myName", newPatient.getPetName().getValue());
+
+ // Parse with stream
+ newPatient = xmlParser.parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
+ assertEquals("myName", newPatient.getPetName().getValue());
+
+ // Check no NPE if base server not configure
+ newPatient = ourCtx.newXmlParser().parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
+ assertNull("myName", newPatient.getPetName().getValue());
+ assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
+ }
+
+ @Test
+ public void testCustomUrlExtensioninBundle() {
+ final String expected = " ";
+
+ final MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
+ patient.setPetName(new StringType("myName"));
+
+ final Bundle bundle = new Bundle();
+ final Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
+ entry.setResource(patient);
+ bundle.addEntry(entry);
+
+ final IParser xmlParser = ourCtx.newXmlParser();
+ xmlParser.setServerBaseUrl("http://www.example.com");
+
+ final String parsedBundle = xmlParser.encodeResourceToString(bundle);
+ System.out.println(parsedBundle);
+ assertEquals(expected, parsedBundle);
+
+ // Parse with string
+ Bundle newBundle = xmlParser.parseResource(Bundle.class, parsedBundle);
+ assertNotNull(newBundle);
+ assertEquals(1, newBundle.getEntry().size());
+ Patient newPatient = (Patient) newBundle.getEntry().get(0).getResource();
+ assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
+
+ // Parse with stream
+ newBundle = xmlParser.parseResource(Bundle.class, new StringReader(parsedBundle));
+ assertNotNull(newBundle);
+ assertEquals(1, newBundle.getEntry().size());
+ newPatient = (Patient) newBundle.getEntry().get(0).getResource();
+ assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
+
+ }
+
+ @Test
+ public void testDuration() {
+ Encounter enc = new Encounter();
+ Duration duration = new Duration();
+ duration.setUnit("day").setValue(123L);
+ enc.setLength(duration);
+
+ String str = ourCtx.newXmlParser().encodeResourceToString(enc);
+ ourLog.info(str);
+
+ assertThat(str, not(containsString("meta")));
+ assertThat(str, containsString(" "));
+ }
+
+ @Test
+ public void testEncodeAndParseBundleWithResourceRefs() {
+
+ Patient pt = new Patient();
+ pt.setId("patid");
+ pt.addName().setFamily("PATIENT");
+
+ Organization org = new Organization();
+ org.setId("orgid");
+ org.setName("ORG");
+ pt.getManagingOrganization().setResource(org);
+
+ Bundle bundle = new Bundle();
+ bundle.addEntry().setResource(pt);
+ bundle.addEntry().setResource(org);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ " "));
+
+ bundle = ourCtx.newXmlParser().parseResource(Bundle.class, encoded);
+ pt = (Patient) bundle.getEntry().get(0).getResource();
+ org = (Organization) bundle.getEntry().get(1).getResource();
+
+ assertEquals("Organization/orgid", org.getIdElement().getValue());
+ assertEquals("Organization/orgid", pt.getManagingOrganization().getReferenceElement().getValue());
+ assertSame(org, pt.getManagingOrganization().getResource());
+ }
+
+ @Test
+ public void testEncodeAndParseCompositeExtension() {
+ PatientWithCustomCompositeExtension pat = new PatientWithCustomCompositeExtension();
+ pat.setId("123");
+ pat.setFooParentExtension(new PatientWithCustomCompositeExtension.FooParentExtension());
+ pat.getFooParentExtension().setChildA(new StringType("ValueA"));
+ pat.getFooParentExtension().setChildB(new StringType("ValueB"));
+
+ String enc = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pat);
+ ourLog.info(enc);
+
+ pat = ourCtx.newXmlParser().parseResource(PatientWithCustomCompositeExtension.class, enc);
+
+ assertEquals("ValueA", pat.getFooParentExtension().getChildA().getValue());
+ assertEquals("ValueB", pat.getFooParentExtension().getChildB().getValue());
+ }
+
+ @Test
+ public void testEncodeAndParseContained() {
+ IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ // Create an organization, note that the organization does not have an ID
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+
+ // Put the organization as a reference in the patient resource
+ patient.getManagingOrganization().setResource(org);
+
+ String encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, containsString(""));
+ assertThat(encoded, containsString(""));
+
+ // Create a bundle with just the patient resource
+ Bundle b = new Bundle();
+ b.addEntry().setResource(patient);
+
+ // Encode the bundle
+ encoded = xmlParser.encodeResourceToString(b);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "", " ")));
+ assertThat(encoded, containsString(""));
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", " ")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", " ", ""))));
+
+ // Re-parse the bundle
+ patient = (Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient));
+ assertEquals("#1", patient.getManagingOrganization().getReference());
+
+ assertNotNull(patient.getManagingOrganization().getResource());
+ org = (Organization) patient.getManagingOrganization().getResource();
+ assertEquals("#1", org.getIdElement().getValue());
+ assertEquals("Contained Test Organization", org.getName());
+
+ // And re-encode a second time
+ encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "", " ", "")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", ""))));
+ assertThat(encoded, containsString(""));
+
+ // And re-encode once more, with the references cleared
+ patient.getContained().clear();
+ patient.getManagingOrganization().setReference((String) null);
+ encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "", " ", "")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", ""))));
+ assertThat(encoded, containsString(""));
+
+ // And re-encode once more, with the references cleared and a manually set local ID
+ patient.getContained().clear();
+ patient.getManagingOrganization().setReference((String) null);
+ patient.getManagingOrganization().getResource().setId(("#333"));
+ encoded = xmlParser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+ assertThat(encoded, stringContainsInOrder(Arrays.asList("", "", " ", "")));
+ assertThat(encoded, not(stringContainsInOrder(Arrays.asList("", ""))));
+
+ }
+
+ @Test
+ public void testEncodeAndParseContainedCustomTypes() {
+ ourCtx = FhirContext.forDstu3();
+ ourCtx.setDefaultTypeForProfile(CustomObservation.PROFILE, CustomObservation.class);
+ ourCtx.setDefaultTypeForProfile(CustomDiagnosticReport.PROFILE, CustomDiagnosticReport.class);
+
+ CustomObservation obs = new CustomObservation();
+ obs.setStatus(Observation.ObservationStatus.FINAL);
+
+ CustomDiagnosticReport dr = new CustomDiagnosticReport();
+ dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
+ dr.addResult().setResource(obs);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setPrettyPrint(true);
+
+ String output = parser.encodeResourceToString(dr);
+ ourLog.info(output);
+
+ assertThat(output, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " "));
+
+ /*
+ * Now PARSE!
+ */
+
+ dr = (CustomDiagnosticReport) parser.parseResource(output);
+ assertEquals(DiagnosticReport.DiagnosticReportStatus.FINAL, dr.getStatus());
+
+ assertEquals("#1", dr.getResult().get(0).getReference());
+ obs = (CustomObservation) dr.getResult().get(0).getResource();
+ assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
+
+ ourCtx = null;
+ }
+
+ @Test
+ public void testEncodeAndParseContainedNonCustomTypes() {
+ ourCtx = FhirContext.forDstu3();
+
+ Observation obs = new Observation();
+ obs.setStatus(Observation.ObservationStatus.FINAL);
+
+ DiagnosticReport dr = new DiagnosticReport();
+ dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
+ dr.addResult().setResource(obs);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setPrettyPrint(true);
+
+ String output = parser.encodeResourceToString(dr);
+ ourLog.info(output);
+
+ assertThat(output, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " "));
+
+ /*
+ * Now PARSE!
+ */
+
+ dr = (DiagnosticReport) parser.parseResource(output);
+ assertEquals(DiagnosticReport.DiagnosticReportStatus.FINAL, dr.getStatus());
+
+ assertEquals("#1", dr.getResult().get(0).getReference());
+ obs = (Observation) dr.getResult().get(0).getResource();
+ assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
+
+ ourCtx = null;
+ }
+
+ @Test
+ public void testEncodeAndParseExtensionOnCode() {
+ Organization o = new Organization();
+ o.setName("ORG");
+ o.addExtension(new Extension("urn:foo", new CodeType("acode")));
+
+ String str = ourCtx.newXmlParser().encodeResourceToString(o);
+ ourLog.info(str);
+ assertThat(str, containsString(""));
+
+ o = ourCtx.newXmlParser().parseResource(Organization.class, str);
+
+ List exts = o.getExtensionsByUrl("urn:foo");
+ assertEquals(1, exts.size());
+ CodeType code = (CodeType) exts.get(0).getValue();
+ assertEquals("acode", code.getValue());
+
+ }
+
+ @Test
+ public void testEncodeAndParseExtensionOnReference() {
+ DataElement de = new DataElement();
+ ElementDefinition.ElementDefinitionBindingComponent b = de.addElement().getBinding();
+ b.setDescription("BINDING");
+
+ Organization o = new Organization();
+ o.setName("ORG");
+ b.addExtension(new Extension("urn:foo", new Reference(o)));
+
+ String str = ourCtx.newXmlParser().encodeResourceToString(de);
+ ourLog.info(str);
+
+ de = ourCtx.newXmlParser().parseResource(DataElement.class, str);
+ b = de.getElement().get(0).getBinding();
+ assertEquals("BINDING", b.getDescription());
+
+ List exts = b.getExtensionsByUrl("urn:foo");
+ assertEquals(1, exts.size());
+ Reference rr = (Reference) exts.get(0).getValue();
+ o = (Organization) rr.getResource();
+ assertEquals("ORG", o.getName());
+
+ }
+
+ @Test
+ public void testEncodeAndParseExtensions() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setUse(Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ Extension ext = new Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
+ patient.addExtension(ext);
+
+ Extension parent = new Extension().setUrl("http://example.com#parent");
+ patient.addExtension(parent);
+ Extension child1 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.addExtension(child1);
+ Extension child2 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value2"));
+ parent.addExtension(child2);
+
+ Extension modExt = new Extension();
+ modExt.setUrl("http://example.com/extensions#modext");
+ modExt.setValue(new DateType("1995-01-02"));
+ patient.addModifierExtension(modExt);
+
+ HumanName name = patient.addName();
+ name.setFamily("Blah");
+ StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given"));
+ given.addExtension(ext2);
+
+ StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent");
+ given2.addExtension(given2ext);
+ given2ext.addExtension(new Extension().setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD")));
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(
+ " "));
+
+ /*
+ * Now parse this back
+ */
+
+ Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, enc);
+ ext = parsed.getExtension().get(0);
+ assertEquals("http://example.com/extensions#someext", ext.getUrl());
+ assertEquals("2011-01-02T11:13:15", ((DateTimeType) ext.getValue()).getValueAsString());
+
+ parent = patient.getExtension().get(1);
+ assertEquals("http://example.com#parent", parent.getUrl());
+ assertNull(parent.getValue());
+ child1 = parent.getExtension().get(0);
+ assertEquals("http://example.com#child", child1.getUrl());
+ assertEquals("value1", ((StringType) child1.getValue()).getValueAsString());
+ child2 = parent.getExtension().get(1);
+ assertEquals("http://example.com#child", child2.getUrl());
+ assertEquals("value2", ((StringType) child2.getValue()).getValueAsString());
+
+ modExt = parsed.getModifierExtension().get(0);
+ assertEquals("http://example.com/extensions#modext", modExt.getUrl());
+ assertEquals("1995-01-02", ((DateType) modExt.getValue()).getValueAsString());
+
+ name = parsed.getName().get(0);
+
+ ext2 = name.getGiven().get(0).getExtension().get(0);
+ assertEquals("http://examples.com#givenext", ext2.getUrl());
+ assertEquals("given", ((StringType) ext2.getValue()).getValueAsString());
+
+ given2ext = name.getGiven().get(1).getExtension().get(0);
+ assertEquals("http://examples.com#givenext_parent", given2ext.getUrl());
+ assertNull(given2ext.getValue());
+ Extension given2ext2 = given2ext.getExtension().get(0);
+ assertEquals("http://examples.com#givenext_child", given2ext2.getUrl());
+ assertEquals("CHILD", ((StringType) given2ext2.getValue()).getValue());
+
+ }
+
+ /**
+ * See #216
+ */
+ @Test
+ public void testEncodeAndParseIdentifierDstu2() {
+ IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("SYS").setValue("VAL").setType(new CodeableConcept().addCoding(new Coding().setSystem("http://hl7.org/fhir/v2/0203").setCode("MR")));
+
+ String out = xmlParser.encodeResourceToString(patient);
+ ourLog.info(out);
+
+ assertThat(out, stringContainsInOrder("",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ " ",
+ "",
+ "",
+ " "));
+
+ patient = ourCtx.newXmlParser().parseResource(Patient.class, out);
+ assertEquals("http://hl7.org/fhir/v2/0203", patient.getIdentifier().get(0).getType().getCoding().get(0).getSystem());
+ assertEquals("MR", patient.getIdentifier().get(0).getType().getCoding().get(0).getCode());
+ }
+
+ private Matcher super String> stringContainsInOrder(java.lang.String... substrings) {
+ return Matchers.stringContainsInOrder(Arrays.asList(substrings));
+ }
+
+ private Matcher super String> stringContainsInOrder(List substrings) {
+ return Matchers.stringContainsInOrder(substrings);
+ }
+
+ /**
+ * See #347
+ */
+ @Test
+ public void testEncodeAndParseMedicationRequest() {
+ MedicationRequest mo = new MedicationRequest();
+ mo.getAuthoredOnElement().setValueAsString("2015-10-05");
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(mo);
+ ourLog.info(encoded);
+
+ mo = ourCtx.newXmlParser().parseResource(MedicationRequest.class, encoded);
+ assertEquals("2015-10-05", mo.getAuthoredOnElement().getValueAsString());
+ }
+
+ @Test
+ public void testEncodeAndParseMetaProfileAndTags() {
+ Patient p = new Patient();
+ p.addName().setFamily("FAMILY");
+
+ p.getMeta().addProfile("http://foo/Profile1");
+ p.getMeta().addProfile("http://foo/Profile2");
+
+ p.getMeta().addTag().setSystem("scheme1").setCode("term1").setDisplay("label1");
+ p.getMeta().addTag().setSystem("scheme2").setCode("term2").setDisplay("label2");
+
+ p.getMeta().addSecurity().setSystem("sec_scheme1").setCode("sec_term1").setDisplay("sec_label1");
+ p.getMeta().addSecurity().setSystem("sec_scheme2").setCode("sec_term2").setDisplay("sec_label2");
+
+ String enc = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+
+ assertThat(enc, stringContainsInOrder("",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " "));
+
+ Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, enc);
+ List gotLabels = parsed.getMeta().getProfile();
+ assertEquals(2, gotLabels.size());
+ UriType label = gotLabels.get(0);
+ assertEquals("http://foo/Profile1", label.getValue());
+ label = gotLabels.get(1);
+ assertEquals("http://foo/Profile2", label.getValue());
+
+ List tagList = parsed.getMeta().getTag();
+ assertEquals(2, tagList.size());
+ assertEquals("scheme1", tagList.get(0).getSystem());
+ assertEquals("term1", tagList.get(0).getCode());
+ assertEquals("label1", tagList.get(0).getDisplay());
+ assertEquals("scheme2", tagList.get(1).getSystem());
+ assertEquals("term2", tagList.get(1).getCode());
+ assertEquals("label2", tagList.get(1).getDisplay());
+
+ tagList = parsed.getMeta().getSecurity();
+ assertEquals(2, tagList.size());
+ assertEquals("sec_scheme1", tagList.get(0).getSystem());
+ assertEquals("sec_term1", tagList.get(0).getCode());
+ assertEquals("sec_label1", tagList.get(0).getDisplay());
+ assertEquals("sec_scheme2", tagList.get(1).getSystem());
+ assertEquals("sec_term2", tagList.get(1).getCode());
+ assertEquals("sec_label2", tagList.get(1).getDisplay());
+ }
+
+ @Test
+ public void testEncodeAndParseMetaProfiles() {
+ Patient p = new Patient();
+ p.addName().setFamily("FAMILY");
+
+ p.getMeta().addTag().setSystem("scheme1").setCode("term1").setDisplay("label1");
+ p.getMeta().addTag().setSystem("scheme2").setCode("term2").setDisplay("label2");
+
+ String enc = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+
+ assertThat(enc, stringContainsInOrder("",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " "));
+
+ Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, enc);
+ assertThat(parsed.getMeta().getProfile(), empty());
+
+ List tagList = parsed.getMeta().getTag();
+ assertEquals(2, tagList.size());
+ assertEquals("scheme1", tagList.get(0).getSystem());
+ assertEquals("term1", tagList.get(0).getCode());
+ assertEquals("label1", tagList.get(0).getDisplay());
+ assertEquals("scheme2", tagList.get(1).getSystem());
+ assertEquals("term2", tagList.get(1).getCode());
+ assertEquals("label2", tagList.get(1).getDisplay());
+ }
+
+ /**
+ * See #336
+ */
+ @Test
+ public void testEncodeAndParseNullPrimitiveWithExtensions() {
+
+ Patient p = new Patient();
+ p.setId("patid");
+ HumanName name = p.addName();
+ name.addGivenElement().setValue(null).setId("f0").addExtension(new Extension("http://foo", new StringType("FOOEXT0")));
+ name.addGivenElement().setValue("V1").setId("f1").addExtension((Extension) new Extension("http://foo", new StringType("FOOEXT1")).setId("ext1id"));
+ name.addGivenElement(); // this one shouldn't get encoded
+ name.addGivenElement().setValue(null).addExtension(new Extension("http://foo", new StringType("FOOEXT3")));
+ name.setId("nameid");
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(output);
+
+ output = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(p);
+ String expected = " ";
+
+ ourLog.info("Expected: {}", expected);
+ ourLog.info("Actual : {}", output);
+
+ assertEquals(expected, output);
+
+ p = ourCtx.newXmlParser().parseResource(Patient.class, output);
+ assertEquals("patid", p.getIdElement().getIdPart());
+
+ name = p.getName().get(0);
+ assertEquals("nameid", name.getId());
+ assertEquals(3, name.getGiven().size());
+
+ assertEquals(null, name.getGiven().get(0).getValue());
+ assertEquals("V1", name.getGiven().get(1).getValue());
+ assertEquals(null, name.getGiven().get(2).getValue());
+
+ assertEquals("f0", name.getGiven().get(0).getId());
+ assertEquals("f1", name.getGiven().get(1).getId());
+ assertEquals(null, name.getGiven().get(2).getId());
+
+ assertEquals(1, name.getGiven().get(0).getExtension().size());
+ assertEquals("http://foo", name.getGiven().get(0).getExtension().get(0).getUrl());
+ assertEquals("FOOEXT0", ((StringType) name.getGiven().get(0).getExtension().get(0).getValue()).getValue());
+ assertEquals(null, name.getGiven().get(0).getExtension().get(0).getId());
+
+ assertEquals(1, name.getGiven().get(1).getExtension().size());
+ assertEquals("http://foo", name.getGiven().get(1).getExtension().get(0).getUrl());
+ assertEquals("FOOEXT1", ((StringType) name.getGiven().get(1).getExtension().get(0).getValue()).getValue());
+ assertEquals("ext1id", name.getGiven().get(1).getExtension().get(0).getId());
+
+ assertEquals(1, name.getGiven().get(2).getExtension().size());
+ assertEquals("http://foo", name.getGiven().get(2).getExtension().get(0).getUrl());
+ assertEquals("FOOEXT3", ((StringType) name.getGiven().get(2).getExtension().get(0).getValue()).getValue());
+ assertEquals(null, name.getGiven().get(2).getExtension().get(0).getId());
+
+ }
+
+ @Test
+ public void testEncodeAndParseProfiledDatatype() {
+ MedicationRequest mo = new MedicationRequest();
+ mo.addDosageInstruction().getTiming().getRepeat().setBounds(new Duration().setCode("code"));
+ String out = ourCtx.newXmlParser().encodeResourceToString(mo);
+ ourLog.info(out);
+ assertThat(out, containsString(""));
+
+ mo = ourCtx.newXmlParser().parseResource(MedicationRequest.class, out);
+ Duration duration = (Duration) mo.getDosageInstruction().get(0).getTiming().getRepeat().getBounds();
+ assertEquals("code", duration.getCode());
+ }
+
+ /**
+ * See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
+ */
+ @Test
+ public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
+ IParser xmlParser = ourCtx.newXmlParser();
+
+ MedicationStatement ms = new MedicationStatement();
+ ms.addDosage().setDose(new SimpleQuantity().setValue(123));
+
+ String output = xmlParser.encodeResourceToString(ms);
+ assertThat(output, containsString(" "));
+ }
+
+ @Test
+ public void testEncodeAndParseSecurityLabels() {
+ Patient p = new Patient();
+ p.addName().setFamily("FAMILY");
+
+ List labels = new ArrayList();
+ labels.add(new Coding().setSystem("SYSTEM1").setCode("CODE1").setDisplay("DISPLAY1").setVersion("VERSION1"));
+ labels.add(new Coding().setSystem("SYSTEM2").setCode("CODE2").setDisplay("DISPLAY2").setVersion("VERSION2"));
+ p.getMeta().getSecurity().addAll(labels);
+
+ String enc = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(enc);
+
+ assertThat(enc, stringContainsInOrder("",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "
",
+ "",
+ "",
+ "",
+ " ",
+ " "));
+
+ Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, enc);
+ List gotLabels = parsed.getMeta().getSecurity();
+
+ assertEquals(2, gotLabels.size());
+
+ Coding label = gotLabels.get(0);
+ assertEquals("SYSTEM1", label.getSystem());
+ assertEquals("CODE1", label.getCode());
+ assertEquals("DISPLAY1", label.getDisplay());
+ assertEquals("VERSION1", label.getVersion());
+
+ label = gotLabels.get(1);
+ assertEquals("SYSTEM2", label.getSystem());
+ assertEquals("CODE2", label.getCode());
+ assertEquals("DISPLAY2", label.getDisplay());
+ assertEquals("VERSION2", label.getVersion());
+ }
+
+ /**
+ * See #103
+ */
+ @Test
+ public void testEncodeAndReEncodeContainedJson() {
+ Composition comp = new Composition();
+ comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
+ comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section1_Allergy0")));
+ comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section2_Allergy0")));
+
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+
+ String string = parser.encodeResourceToString(comp);
+ ourLog.info(string);
+
+ Composition parsed = parser.parseResource(Composition.class, string);
+ parsed.getSection().remove(0);
+
+ string = parser.encodeResourceToString(parsed);
+ ourLog.info(string);
+
+ parsed = parser.parseResource(Composition.class, string);
+ assertEquals(2, parsed.getContained().size());
+ }
+
+ /**
+ * See #103
+ */
+ @Test
+ public void testEncodeAndReEncodeContainedXml() {
+ Composition comp = new Composition();
+ comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
+ comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section1_Allergy0")));
+ comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section2_Allergy0")));
+
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ String string = parser.encodeResourceToString(comp);
+ ourLog.info(string);
+
+ Composition parsed = parser.parseResource(Composition.class, string);
+ parsed.getSection().remove(0);
+
+ string = parser.encodeResourceToString(parsed);
+ ourLog.info(string);
+
+ parsed = parser.parseResource(Composition.class, string);
+ assertEquals(2, parsed.getContained().size());
+ }
+
+ @Test
+ public void testEncodeBinaryWithNoContentType() {
+ Binary b = new Binary();
+ b.setContent(new byte[]{1, 2, 3, 4});
+
+ String output = ourCtx.newXmlParser().encodeResourceToString(b);
+ ourLog.info(output);
+
+ assertEquals(" ", output);
+ }
+
+ @Test
+ public void testEncodeBinaryWithSecurityContext() {
+ Binary bin = new Binary();
+ bin.setContentType("text/plain");
+ bin.setContent("Now is the time".getBytes());
+ Reference securityContext = new Reference();
+ securityContext.setReference("DiagnosticReport/1");
+ bin.setSecurityContext(securityContext);
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(bin);
+ ourLog.info(encoded);
+ assertThat(encoded, containsString("Binary"));
+ assertThat(encoded, containsString(""));
+ assertThat(encoded, containsString(" "));
+ assertThat(encoded, containsString(""));
+ }
+
+ @Test
+ public void testEncodeBundleWithContained() {
+ DiagnosticReport rpt = new DiagnosticReport();
+ rpt.addResult().setResource(new Observation().setCode(new CodeableConcept().setText("Sharp1")).setId("#1"));
+ rpt.addResult().setResource(new Observation().setCode(new CodeableConcept().setText("Uuid1")).setId("urn:uuid:UUID1"));
+
+ Bundle b = new Bundle();
+ b.addEntry().setResource(rpt);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder("",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ " "));
+
+ }
+
+ /**
+ * See #113
+ */
+ @Test
+ public void testEncodeContainedResources() {
+
+ MedicationRequest medicationPrescript = new MedicationRequest();
+
+ String medId = "123";
+ CodeableConcept codeDt = new CodeableConcept().addCoding(new Coding().setSystem("urn:sys").setCode("code1"));
+
+ // Adding medication to Contained.
+ Medication medResource = new Medication();
+ medResource.setCode(codeDt);
+ medResource.setId("#" + String.valueOf(medId));
+ medicationPrescript.getContained().add(medResource);
+
+ // Medication reference. This should point to the contained resource.
+ Reference medRefDt = new Reference("#" + medId);
+ medRefDt.setDisplay("MedRef");
+ medicationPrescript.setMedication(medRefDt);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String encoded = p.encodeResourceToString(medicationPrescript);
+ ourLog.info(encoded);
+
+ // @formatter:on
+ assertThat(encoded,
+ stringContainsInOrder("", "", "", "", "", "",
+ "", "", "
", "
", " ", " ", "", "",
+ "", " ", " "));
+
+ }
+
+ /**
+ * See #113
+ */
+ @Test
+ public void testEncodeContainedResourcesAutomatic() {
+
+ MedicationRequest medicationPrescript = new MedicationRequest();
+ String nameDisp = "MedRef";
+ CodeableConcept codeDt = new CodeableConcept().addCoding(new Coding("urn:sys", "code1", null));
+
+ // Adding medication to Contained.
+ Medication medResource = new Medication();
+ // No ID set
+ medResource.setCode(codeDt);
+
+ // Medication reference. This should point to the contained resource.
+ Reference medRefDt = new Reference();
+ medRefDt.setDisplay(nameDisp);
+ // Resource reference set, but no ID
+ medRefDt.setResource(medResource);
+ medicationPrescript.setMedication(medRefDt);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String encoded = p.encodeResourceToString(medicationPrescript);
+ ourLog.info(encoded);
+
+ assertThat(encoded,
+ stringContainsInOrder("", "", "", "", "", "",
+ "", "", "
", "
", " ", " ", "", "",
+ "", " ", " "));
+
+ }
+
+ /**
+ * See #113
+ */
+ @Test
+ public void testEncodeContainedResourcesManualContainUsingNonLocalId() {
+
+ MedicationRequest medicationPrescript = new MedicationRequest();
+
+ String medId = "123";
+ CodeableConcept codeDt = new CodeableConcept().addCoding(new Coding("urn:sys", "code1", null));
+
+ // Adding medication to Contained.
+ Medication medResource = new Medication();
+ medResource.setCode(codeDt);
+ medResource.setId(String.valueOf(medId)); // ID does not start with '#'
+ medicationPrescript.getContained().add(medResource);
+
+ // Medication reference. This should point to the contained resource.
+ Reference medRefDt = new Reference("#" + medId);
+ medRefDt.setDisplay("MedRef");
+ medicationPrescript.setMedication(medRefDt);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String encoded = p.encodeResourceToString(medicationPrescript);
+ ourLog.info(encoded);
+
+ assertThat(encoded,
+ stringContainsInOrder("", "", "", "", "", "",
+ "", "", "
", "
", " ", " ", "", "",
+ "", " ", " "));
+
+ }
+
+ @Test
+ public void testEncodeContainedWithNarrativeIsSuppresed() throws Exception {
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ // Create an organization, note that the organization does not have an ID
+ Organization org = new Organization();
+ org.getNameElement().setValue("Contained Test Organization");
+ org.getText().setDivAsString("FOOBAR");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+ patient.getText().setDivAsString("BARFOO");
+ patient.getManagingOrganization().setResource(org);
+
+ String encoded = parser.encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder("", "BARFOO", "", "", "", ""));
+ assertThat(encode, stringContainsInOrder("\n\nA P TAG
line1\nline2\nline3 BOLD
", + "
line1\nline2\nline3 BOLD")); + + } + + @Test + public void testEncodeDivWithPrePrettyPrint() { + + Patient p = new Patient(); + p.getText().setDivAsString("
A P TAG
line1\nline2\nline3 BOLD
", ""));
+ assertThat(encoded, not(containsString("text")));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, containsString("maritalStatus"));
+ }
+
+ @Test
+ public void testEncodeNonContained() {
+ // Create an organization
+ Organization org = new Organization();
+ org.setId("Organization/65546");
+ org.getNameElement().setValue("Contained Test Organization");
+
+ // Create a patient
+ Patient patient = new Patient();
+ patient.setId("Patient/1333");
+ patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
+ patient.getManagingOrganization().setResource(org);
+
+ // Create a list containing both resources. In a server method, you might just
+ // return this list, but here we will create a bundle to encode.
+ List
", ""));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeSummary2() {
+ Patient patient = new Patient();
+ patient.setId("Patient/1/_history/1");
+ patient.getText().setDivAsString("", ""));
+ assertThat(encoded, stringContainsInOrder("", " ",
+ "
", " "));
+ assertThat(encoded, not(containsString("THE DIV")));
+ assertThat(encoded, containsString("family"));
+ assertThat(encoded, not(containsString("maritalStatus")));
+ }
+
+ @Test
+ public void testEncodeUndeclaredBlock() throws Exception {
+ FooMessageHeader.FooMessageSourceComponent source = new FooMessageHeader.FooMessageSourceComponent();
+ source.getMessageHeaderApplicationId().setValue("APPID");
+ source.setName("NAME");
+
+ FooMessageHeader header = new FooMessageHeader();
+ header.setSource(source);
+
+ header.addDestination().setName("DEST");
+
+ Bundle bundle = new Bundle();
+ bundle.addEntry().setResource(header);
+
+ IParser p = ourCtx.newXmlParser();
+ p.setPrettyPrint(true);
+
+ String encode = p.encodeResourceToString(bundle);
+ ourLog.info(encode);
+
+ assertThat(encode, containsString(""));
+ assertThat(encode, stringContainsInOrder(" fact = new Address.AddressUseEnumFactory();
+ PrimitiveType enumeration = new Enumeration(fact).setValue(Address.AddressUse.HOME);
+ patient.addExtension().setUrl("urn:foo").setValue(enumeration);
+
+ String val = parser.encodeResourceToString(patient);
+ ourLog.info(val);
+ assertThat(val, StringContains.containsString(" "));
+
+ MyPatientWithOneDeclaredEnumerationExtensionDstu3 actual = parser.parseResource(MyPatientWithOneDeclaredEnumerationExtensionDstu3.class, val);
+ assertEquals(Address.AddressUse.HOME, patient.getAddress().get(0).getUse());
+ Enumeration ref = actual.getFoo();
+ assertEquals("home", ref.getValue().toCode());
+
+ }
+
+ @Test
+ public void testEncodeWithContained() {
+ List contained = new ArrayList();
+
+ // Will be added by reference
+ Patient p = new Patient();
+ p.setId("#" + "1000");
+ contained.add(p);
+
+ // Will be added by direct resource object
+ Location l = new Location();
+ l.setId("#" + "1001");
+ contained.add(l);
+
+ // Will not be referred to (and therefore shouldn't appear in output)
+ Location l2 = new Location();
+ l2.setId("#1002");
+ contained.add(l2);
+
+ Appointment appointment = new Appointment();
+ appointment.setId("1234");
+ appointment.getContained().addAll(contained);
+
+ appointment.addParticipant().getActor().setReference("#1000");
+ appointment.addParticipant().getActor().setResource(l);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(appointment);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ " "));
+
+ assertThat(encoded, not(containsString("#1002")));
+ }
+
+ @Test
+ public void testEncodeWithDontEncodeElements() throws Exception {
+ Patient patient = new Patient();
+ patient.setId("123");
+ patient.getMeta().addProfile("http://profile");
+ patient.addName().setFamily("FAMILY").addGiven("GIVEN");
+ patient.addAddress().addLine("LINE1");
+
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("*.meta", "*.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.meta", "Patient.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.name.family"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("GIVEN"));
+ assertThat(out, not(containsString("FAMILY")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("*.meta", "*.id"));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("address"));
+ assertThat(out, not(containsString("id")));
+ assertThat(out, not(containsString("meta")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setDontEncodeElements(Sets.newHashSet("Patient.meta"));
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient.name")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(patient);
+ ourLog.info(out);
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, containsString("id"));
+ assertThat(out, not(containsString("address")));
+ assertThat(out, not(containsString("meta")));
+ }
+ }
+
+ @Test
+ public void testEncodeWithEncodeElements() throws Exception {
+ Patient patient = new Patient();
+ patient.getMeta().addProfile("http://profile");
+ patient.addName().setFamily("FAMILY");
+ patient.addAddress().addLine("LINE1");
+
+ Bundle bundle = new Bundle();
+ bundle.setTotal(100);
+ bundle.addEntry().setResource(patient);
+
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet<>(Arrays.asList("Patient.name", "Bundle.entry")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, not(containsString("total")));
+ assertThat(out, (containsString("Patient")));
+ assertThat(out, (containsString("name")));
+ assertThat(out, not(containsString("address")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient.name")));
+ p.setEncodeElementsAppliesToResourceTypes(new HashSet(Arrays.asList("Patient")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, (containsString("total")));
+ assertThat(out, (containsString("Patient")));
+ assertThat(out, (containsString("name")));
+ assertThat(out, not(containsString("address")));
+ }
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet(Arrays.asList("Patient")));
+ p.setEncodeElementsAppliesToResourceTypes(new HashSet(Arrays.asList("Patient")));
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, (containsString("total")));
+ assertThat(out, (containsString("Patient")));
+ assertThat(out, (containsString("name")));
+ assertThat(out, (containsString("address")));
+ }
+
+ }
+
+ @Test
+ public void testEncodeWithEncodeElementsAppliesToChildResourcesOnly() throws Exception {
+ Patient patient = new Patient();
+ patient.getMeta().addProfile("http://profile");
+ patient.addName().setFamily("FAMILY");
+ patient.addAddress().addLine("LINE1");
+
+ Bundle bundle = new Bundle();
+ bundle.setTotal(100);
+ bundle.addEntry().setResource(patient);
+
+ {
+ IParser p = ourCtx.newXmlParser();
+ p.setEncodeElements(new HashSet<>(Arrays.asList("Patient.name")));
+ p.setEncodeElementsAppliesToChildResourcesOnly(true);
+ p.setPrettyPrint(true);
+ String out = p.encodeResourceToString(bundle);
+ ourLog.info(out);
+ assertThat(out, containsString("total"));
+ assertThat(out, containsString("Patient"));
+ assertThat(out, containsString("name"));
+ assertThat(out, not(containsString("address")));
+ }
+ }
+
+ /**
+ * Test for the url generated based on the server config
+ */
+ @Test
+ public void testGeneratedUrls() {
+ final IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
+ xmlParser.setServerBaseUrl("http://myserver.com");
+
+ final CustomPatientDstu3 patient = new CustomPatientDstu3();
+ patient.setHomeless(new BooleanType(true));
+
+ final String parsedPatient = xmlParser.encodeResourceToString(patient);
+
+ assertTrue(parsedPatient.contains(""));
+ assertTrue(parsedPatient.contains(""));
+ }
+
+ @Test
+ public void testMoreExtensions() throws Exception {
+
+ Patient patient = new Patient();
+ patient.addIdentifier().setUse(Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
+
+ Extension ext = new Extension();
+ ext.setUrl("http://example.com/extensions#someext");
+ ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
+
+ // Add the extension to the resource
+ patient.addExtension(ext);
+ // END SNIPPET: resourceExtension
+
+ // START SNIPPET: resourceStringExtension
+ HumanName name = patient.addName();
+ name.setFamily("Shmoe");
+ StringType given = name.addGivenElement();
+ given.setValue("Joe");
+ Extension ext2 = new Extension().setUrl("http://examples.com#givenext").setValue(new StringType("given"));
+ given.addExtension(ext2);
+
+ StringType given2 = name.addGivenElement();
+ given2.setValue("Shmoe");
+ Extension given2ext = new Extension().setUrl("http://examples.com#givenext_parent");
+ given2.addExtension(given2ext);
+ Extension givenExtChild = new Extension();
+ givenExtChild.setUrl("http://examples.com#givenext_child").setValue(new StringType("CHILD"));
+ given2ext.addExtension(givenExtChild);
+ // END SNIPPET: resourceStringExtension
+
+ // START SNIPPET: subExtension
+ Extension parent = new Extension().setUrl("http://example.com#parent");
+ patient.addExtension(parent);
+
+ Extension child1 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.addExtension(child1);
+
+ Extension child2 = new Extension().setUrl("http://example.com#child").setValue(new StringType("value1"));
+ parent.addExtension(child2);
+ // END SNIPPET: subExtension
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(output);
+
+ String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(
+ " "));
+ assertThat(enc, containsString(" "));
+ assertThat(enc, containsString(
+ " "));
+ }
+
+ @Test
+ public void testOmitResourceId() {
+ Patient p = new Patient();
+ p.setId("123");
+ p.addName().setFamily("ABC");
+
+ assertThat(ourCtx.newXmlParser().encodeResourceToString(p), stringContainsInOrder("123", "ABC"));
+ assertThat(ourCtx.newXmlParser().setOmitResourceId(true).encodeResourceToString(p), containsString("ABC"));
+ assertThat(ourCtx.newXmlParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnFhirContext() {
+ try {
+ String tmp = " ";
+ ourCtx.getParserOptions().setOverrideResourceIdWithBundleEntryFullUrl(false);
+ Bundle bundle = (Bundle) ourCtx.newXmlParser().parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = null;
+ }
+ }
+
+ @Test
+ public void testOverrideResourceIdWithBundleEntryFullUrlDisabled_ConfiguredOnParser() {
+ try {
+ String tmp = " ";
+ Bundle bundle = (Bundle) ourCtx.newXmlParser().setOverrideResourceIdWithBundleEntryFullUrl(false).parseResource(tmp);
+ assertEquals(1, bundle.getEntry().size());
+ {
+ Patient o1 = (Patient) bundle.getEntry().get(0).getResource();
+ IIdType o1Id = o1.getIdElement();
+ assertFalse(o1Id.hasBaseUrl());
+ assertEquals("Patient", o1Id.getResourceType());
+ assertEquals("patxuzos", o1Id.getIdPart());
+ assertFalse(o1Id.hasVersionIdPart());
+ }
+ } finally {
+ // ensure we cleanup ourCtx so other tests continue to work
+ ourCtx = null;
+ }
+ }
+
+ @Test
+ public void testParseAndEncodeComments() {
+ String input = "\n" +
+ " " +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321
\n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " " +
+ " " +
+ " ";
+
+ Patient res = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ res.getFormatCommentsPre();
+ assertEquals("Patient/pat1", res.getId());
+ assertEquals("654321", res.getIdentifier().get(0).getValue());
+ assertEquals(true, res.getActive());
+
+ assertThat(res.getIdElement().getFormatCommentsPre(), contains("pre resource comment"));
+ assertThat(res.getIdentifier().get(0).getFormatCommentsPre(), contains("identifier comment 1", "identifier comment 2"));
+ assertThat(res.getIdentifier().get(0).getUseElement().getFormatCommentsPre(), contains("use comment 1", "use comment 2"));
+ assertThat(res.getActiveElement().getFormatCommentsPost(), contains("post resource comment"));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(res);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder(
+ "\"identifier\": [",
+ "{",
+ "\"fhir_comments\":",
+ "[",
+ "\"identifier comment 1\"",
+ ",",
+ "\"identifier comment 2\"",
+ "]",
+ "\"use\": \"usual\",",
+ "\"_use\": {",
+ "\"fhir_comments\":",
+ "[",
+ "\"use comment 1\"",
+ ",",
+ "\"use comment 2\"",
+ "]",
+ "},",
+ "\"type\""));
+
+ encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ " ",
+ "Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321
",
+ "",
+ " ",
+ " \n",
+ " ",
+ "",
+ "",
+ "",
+ " ",
+ "",
+ " "));
+
+ }
+
+ @Test
+ public void testParseAndEncodeCommentsOnExtensions() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Patient pat = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pat);
+ ourLog.info(output);
+
+ assertThat(output, stringContainsInOrder(
+ "",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "));
+
+ output = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(pat);
+ ourLog.info(output);
+
+ assertThat(output, stringContainsInOrder(
+ "{",
+ " \"resourceType\": \"Patient\",",
+ " \"id\": \"someid\",",
+ " \"_id\": {",
+ " \"fhir_comments\": [",
+ " \" comment 1 \"",
+ " ]",
+ " },",
+ " \"extension\": [",
+ " {",
+ " \"fhir_comments\": [",
+ " \" comment 2 \",",
+ " \" comment 7 \"",
+ " ],",
+ " \"url\": \"urn:patientext:att\",",
+ " \"valueAttachment\": {",
+ " \"fhir_comments\": [",
+ " \" comment 3 \",",
+ " \" comment 6 \"",
+ " ],",
+ " \"contentType\": \"aaaa\",",
+ " \"_contentType\": {",
+ " \"fhir_comments\": [",
+ " \" comment 4 \"",
+ " ]",
+ " },",
+ " \"data\": \"AAAA\",",
+ " \"_data\": {",
+ " \"fhir_comments\": [",
+ " \" comment 5 \"",
+ " ]",
+ " }",
+ " }",
+ " }",
+ " ]",
+ "}"));
+
+ }
+
+ @Test
+ public void testParseAndEncodeExtensionOnReference() {
+
+ String input = "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ " " +
+ "
" +
+ " " +
+ " " +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "
" +
+ " " +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "
" +
+ "
" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ "" +
+ " " +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ " " +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ " " +
+ "" +
+ "" +
+ " " +
+ "" +
+ " " +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ "" +
+ "" +
+ " " +
+ " " +
+ "" +
+ " " +
+ " " +
+ " " +
+ " ";
+
+ DataElement de = ourCtx.newXmlParser().parseResource(DataElement.class, input);
+ String output = ourCtx.newXmlParser().encodeResourceToString(de).replace(" xmlns=\"http://hl7.org/fhir\"", "");
+
+ ElementDefinition elem = de.getElement().get(0);
+ ElementDefinition.ElementDefinitionBindingComponent b = elem.getBinding();
+ // assertEquals("All codes representing the gender of a person.", b.getDescription());
+
+ Reference ref = (Reference) b.getValueSet();
+ assertEquals("#2179414", ref.getReference());
+
+ assertEquals(2, ref.getExtension().size());
+ Extension ext = ref.getExtension().get(0);
+ assertEquals("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset", ext.getUrl());
+ assertEquals(Reference.class, ext.getValue().getClass());
+ assertEquals("#2179414-permitted", ((Reference) ext.getValue()).getReference());
+ assertEquals(ValueSet.class, ((Reference) ext.getValue()).getResource().getClass());
+
+ ext = ref.getExtension().get(1);
+ assertEquals("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-conceptmap", ext.getUrl());
+ assertEquals(Reference.class, ext.getValue().getClass());
+ assertEquals("#2179414-cm", ((Reference) ext.getValue()).getReference());
+ assertEquals(ConceptMap.class, ((Reference) ext.getValue()).getResource().getClass());
+
+ ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(de));
+
+ assertThat(output, containsString("http://hl7.org/fhir/StructureDefinition/11179-permitted-value-valueset"));
+
+ }
+
+ @Test
+ public void testParseAndEncodeNestedExtensions() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Patient p = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ DateType bd = p.getBirthDateElement();
+ assertEquals("2005-03-04", bd.getValueAsString());
+
+ List exts = bd.getExtensionsByUrl("http://my.fancy.extension.url");
+ assertEquals(1, exts.size());
+ Extension ext = exts.get(0);
+ assertEquals(null, ext.getValue());
+
+ exts = ext.getExtensionsByUrl("http://my.fancy.extension.url");
+ assertEquals(1, exts.size());
+ ext = exts.get(0);
+ assertEquals("myNestedValue", ((StringType) ext.getValue()).getValue());
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(encoded);
+
+ assertThat(encoded, stringContainsInOrder(
+ "",
+ "",
+ "",
+ "",
+ "",
+ " ",
+ " ",
+ " ",
+ " "));
+
+ }
+
+ @Test
+ public void testParseBundleNewWithPlaceholderIds() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:oid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+
+ }
+
+ @Test
+ public void testParseBundleNewWithPlaceholderIdsInBase1() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:oid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+ }
+
+ @Test
+ public void testParseBundleNewWithPlaceholderIdsInBase2() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:uuid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+
+ input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+
+ parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("urn:uuid:0.1.2.3", parsed.getEntry().get(0).getResource().getIdElement().getValue());
+
+ }
+
+ @Test
+ public void testParseBundleOldStyleWithUnknownLinks() throws Exception {
+
+ String bundle = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Bundle b = (Bundle) ourCtx.newXmlParser().parseResource(bundle);
+ assertEquals(1, b.getEntry().size());
+
+ }
+
+ @Test
+ public void testParseBundleOldWithPlaceholderIds() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n";
+
+ Bundle parsed = (Bundle) ourCtx.newXmlParser().parseResource(input);
+ assertEquals("urn:oid:0.1.2.3", parsed.getEntry().get(0).getResource().getId());
+
+ }
+
+ @Test
+ public void testParseBundleWithBinary() {
+ // TODO: implement this test, make sure we handle ID and meta correctly in Binary
+ }
+
+ /**
+ * See #191
+ */
+ @Test
+ public void testParseBundleWithLinksOfUnknownRelation() throws Exception {
+ String input = IOUtils.toString(Dstu3XmlParserTest.class.getResourceAsStream("/bundle_orion.xml"), StandardCharsets.UTF_8);
+ Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+
+ Bundle.BundleLinkComponent link = parsed.getLink().get(0);
+ assertEquals("just trying add link", link.getRelation());
+ assertEquals("blarion", link.getUrl());
+
+ Bundle.BundleEntryComponent entry = parsed.getEntry().get(0);
+ link = entry.getLink().get(0);
+ assertEquals("orionhealth.edit", link.getRelation());
+ assertEquals("Observation", link.getUrl());
+ }
+
+ @Test
+ public void testParseBundleWithResourceId() {
+
+ String input = ""
+ + " "
+ + " "
+ + " "
+ + " \n";
+
+ Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+ assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/3", bundle.getEntry().get(0).getResource().getIdElement().getValue());
+ assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/2", bundle.getEntry().get(1).getResource().getIdElement().getValue());
+ assertEquals("http://localhost:58402/fhir/context/Patient/1/_history/1", bundle.getEntry().get(2).getResource().getIdElement().getValue());
+ }
+
+
+ /**
+ * Thanks to Alexander Kley!
+ */
+ @Test
+ public void testParseContainedBinaryResource() {
+ byte[] bin = new byte[]{0, 1, 2, 3, 4};
+ final Binary binary = new Binary();
+ binary.setContentType("PatientConsent").setContent(bin);
+
+ DocumentManifest manifest = new DocumentManifest();
+ CodeableConcept cc = new CodeableConcept();
+ cc.addCoding().setSystem("mySystem").setCode("PatientDocument");
+ manifest.setType(cc);
+ manifest.setMasterIdentifier(new Identifier().setSystem("mySystem").setValue(UUID.randomUUID().toString()));
+ manifest.addContent().setP(new Reference(binary));
+ manifest.setStatus(Enumerations.DocumentReferenceStatus.CURRENT);
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(manifest);
+ ourLog.info(encoded);
+ assertThat(encoded, StringContainsInOrder.stringContainsInOrder(Arrays.asList("contained>", "")));
+
+ DocumentManifest actual = ourCtx.newXmlParser().parseResource(DocumentManifest.class, encoded);
+ assertEquals(1, actual.getContained().size());
+ assertEquals(1, actual.getContent().size());
+
+ /*
+ * If this fails, it's possibe the DocumentManifest structure is wrong: It should be
+ *
+ * @Child(name = "p", type = {Attachment.class, ValueSet.class}, order=1, min=1, max=1, modifier=false, summary=true)
+ */
+ assertNotNull(((Reference) actual.getContent().get(0).getP()).getResource());
+ }
+
+ /**
+ * See #426
+ */
+ @Test
+ public void testParseExtensionWithIdType() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input);
+
+ List extList = pt.getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare");
+ extList = extList.get(0).getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare-aaa-id");
+ Extension ext = extList.get(0);
+ IdType value = (IdType) ext.getValue();
+ assertEquals("mc1", value.getValueAsString());
+ }
+
+ /**
+ * See #426
+ *
+ * Value type of FOO isn't a valid datatype
+ */
+ @Test
+ public void testParseExtensionWithInvalidType() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Patient pt = ourCtx.newXmlParser().parseResource(Patient.class, input);
+
+ List extList = pt.getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare");
+ extList = extList.get(0).getExtensionsByUrl("http://aaa.ch/fhir/Patient#mangedcare-aaa-id");
+ Extension ext = extList.get(0);
+ IdType value = (IdType) ext.getValue();
+ assertEquals(null, value);
+ }
+
+ /**
+ * See #342
+ */
+ @Test(expected = DataFormatException.class)
+ public void testParseInvalid() {
+ ourCtx.newXmlParser().parseResource("FOO");
+ }
+
+ /**
+ * See #366
+ */
+ @Test()
+ public void testParseInvalidBoolean() {
+
+ String resource = "\n" +
+ " \n" +
+ " ";
+
+ IParser p = ourCtx.newXmlParser();
+
+ try {
+ p.parseResource(resource);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"1\": Invalid boolean string: '1'", e.getMessage());
+ }
+
+ LenientErrorHandler errorHandler = new LenientErrorHandler();
+ assertEquals(true, errorHandler.isErrorOnInvalidValue());
+ errorHandler.setErrorOnInvalidValue(false);
+ p.setParserErrorHandler(errorHandler);
+ }
+
+ @Test
+ public void testParseInvalidTextualNumber() {
+ Observation obs = new Observation();
+ obs.setValue(new Quantity().setValue(1234));
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs);
+ encoded = encoded.replace("1234", "\"1234\"");
+ ourLog.info(encoded);
+ ourCtx.newJsonParser().parseResource(encoded);
+ }
+
+ @Test
+ public void testParseMetaUpdatedDate() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Bundle b = ourCtx.newXmlParser().parseResource(Bundle.class, input);
+
+ InstantType updated = b.getMeta().getLastUpdatedElement();
+ assertEquals("2015-06-22T15:48:57.554-04:00", updated.getValueAsString());
+
+ }
+
+ @Test
+ public void testParseMetadata() throws Exception {
+
+ String content = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Bundle b = ourCtx.newXmlParser().parseResource(Bundle.class, content);
+ assertEquals(1, b.getEntry().size());
+
+ Bundle.BundleEntryComponent entry = b.getEntry().get(0);
+ Patient pt = (Patient) entry.getResource();
+ assertEquals("http://foo/fhirBase2/Patient/1/_history/2", pt.getIdElement().getValue());
+ assertEquals("2012-01-02", pt.getBirthDateElement().getValueAsString());
+ assertEquals("0.123", entry.getSearch().getScore().toString());
+ assertEquals("match", entry.getSearch().getMode().toCode());
+ assertEquals("POST", entry.getRequest().getMethod().toCode());
+ assertEquals("http://foo/Patient?identifier=value", entry.getRequest().getUrl());
+ assertEquals("2001-02-22T09:22:33-07:00", pt.getMeta().getLastUpdatedElement().getValueAsString());
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String reEncoded = p.encodeResourceToString(b);
+ ourLog.info(reEncoded);
+
+ compareXml(content, reEncoded);
+
+ }
+
+ @Test
+ public void testParseNestedExtensionsInvalid() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ try {
+ ourCtx.newXmlParser().parseResource(Patient.class, input);
+ fail();
+ } catch (DataFormatException e) {
+ assertThat(e.getMessage(), containsString("Extension (URL='http://my.fancy.extension.url') must not have both a value and other contained extensions"));
+ }
+ }
+
+ /**
+ * See #163
+ */
+ @Test
+ public void testParseResourceType() {
+ IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true);
+
+ // Patient
+ Patient patient = new Patient();
+ String patientId = UUID.randomUUID().toString();
+ patient.setId(new IdType("Patient", patientId));
+ patient.addName().addGiven("John").setFamily("Smith");
+ patient.setGender(Enumerations.AdministrativeGender.MALE);
+ patient.setBirthDateElement(new DateType("1987-04-16"));
+
+ // Bundle
+ Bundle bundle = new Bundle();
+ bundle.setType(Bundle.BundleType.COLLECTION);
+ bundle.addEntry().setResource(patient);
+
+ String bundleText = xmlParser.encodeResourceToString(bundle);
+ ourLog.info(bundleText);
+
+ Bundle reincarnatedBundle = xmlParser.parseResource(Bundle.class, bundleText);
+ Patient reincarnatedPatient = (Patient) reincarnatedBundle.getEntry().get(0).getResource();
+
+ assertEquals("Patient", patient.getIdElement().getResourceType());
+ assertEquals("Patient", reincarnatedPatient.getIdElement().getResourceType());
+ }
+
+ @Test(expected = DataFormatException.class)
+ public void testParseWithInvalidLocalRef() throws IOException {
+ String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ parser.parseResource(Bundle.class, string);
+ }
+
+ @Test()
+ public void testParseWithInvalidLocalRefLenient() throws IOException {
+ String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new LenientErrorHandler());
+ parser.parseResource(Bundle.class, string);
+ }
+
+ /**
+ * If a contained resource refers to a contained resource that comes after it, it should still be successfully
+ * woven together.
+ */
+ @Test
+ public void testParseWovenContainedResources() throws IOException {
+ String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_woven_obs.xml"), StandardCharsets.UTF_8);
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ org.hl7.fhir.dstu3.model.Bundle bundle = parser.parseResource(Bundle.class, string);
+
+ DiagnosticReport resource = (DiagnosticReport) bundle.getEntry().get(0).getResource();
+ Observation obs = (Observation) resource.getResult().get(1).getResource();
+ assertEquals("#2", obs.getId());
+ Reference performerFirstRep = obs.getPerformerFirstRep();
+ Practitioner performer = (Practitioner) performerFirstRep.getResource();
+ assertEquals("#3", performer.getId());
+ }
+
+ /**
+ * See #414
+ */
+ @Test
+ public void testParseXmlExtensionWithoutUrl() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new LenientErrorHandler());
+ Patient parsed = (Patient) parser.parseResource(input);
+ assertEquals(1, parsed.getExtension().size());
+ assertEquals(null, parsed.getExtension().get(0).getUrl());
+ assertEquals("2011-01-02T11:13:15", parsed.getExtension().get(0).getValueAsPrimitive().getValueAsString());
+
+ try {
+ parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ parser.parseResource(input);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Resource is missing required element 'url' in parent element 'extension'", e.getCause().getMessage());
+ }
+
+ }
+
+ /**
+ * See #414
+ */
+ @Test
+ public void testParseXmlModifierExtensionWithoutUrl() {
+
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new LenientErrorHandler());
+ Patient parsed = (Patient) parser.parseResource(input);
+ assertEquals(1, parsed.getModifierExtension().size());
+ assertEquals(null, parsed.getModifierExtension().get(0).getUrl());
+ assertEquals("2011-01-02T11:13:15", parsed.getModifierExtension().get(0).getValueAsPrimitive().getValueAsString());
+
+ try {
+ parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+ parser.parseResource(input);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("Resource is missing required element 'url' in parent element 'modifierExtension'", e.getCause().getMessage());
+ }
+
+ }
+
+ /**
+ * See #344
+ */
+ @Test
+ public void testParserIsCaseSensitive() {
+ Observation obs = new Observation();
+ SampledData data = new SampledData();
+ data.setData("1 2 3");
+ data.setOrigin((SimpleQuantity) new SimpleQuantity().setValue(0L));
+ data.setPeriod(1000L);
+ obs.setValue(data);
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true).setParserErrorHandler(new StrictErrorHandler());
+ String encoded = p.encodeResourceToString(obs);
+ ourLog.info(encoded);
+
+ p.parseResource(encoded);
+
+ try {
+ p.parseResource(encoded.replace("Observation", "observation"));
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [1,1]]: Unknown resource type 'observation': Resource names are case sensitive, found similar name: 'Observation'",
+ e.getMessage());
+ }
+
+ try {
+ p.parseResource(encoded.replace("valueSampledData", "valueSampleddata"));
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Unknown element 'valueSampleddata' found during parse", e.getMessage());
+ }
+ }
+
+ /**
+ * See #551
+ */
+ @Test
+ public void testXmlLargeAttribute() {
+ String largeString = StringUtils.leftPad("", (int) FileUtils.ONE_MB, 'A');
+
+ Patient p = new Patient();
+ p.addName().setFamily(largeString);
+
+ String encoded = ourCtx.newXmlParser().encodeResourceToString(p);
+
+ p = ourCtx.newXmlParser().parseResource(Patient.class, encoded);
+
+ assertEquals(largeString, p.getNameFirstRep().getFamily());
+ }
+
+ /**
+ * See #339
+ *
+ * https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
+ */
+ @Test
+ public void testXxe() {
+
+ String input = "" +
+ "" +
+ "]>" +
+ "" +
+ "" +
+ "TEXT &xxe; TEXT" +
+ " " +
+ "" +
+ "" +
+ " " +
+ " ";
+
+ ourLog.info(input);
+
+ try {
+ ourCtx.newXmlParser().parseResource(Patient.class, input);
+ fail();
+ } catch (DataFormatException e) {
+ assertThat(e.toString(), containsString("Undeclared general entity"));
+ }
+
+ }
+
+ public static void compareXml(String content, String reEncoded) {
+ Diff d = DiffBuilder.compare(Input.fromString(content))
+ .withTest(Input.fromString(reEncoded))
+ .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
+ .checkForSimilar()
+ .ignoreWhitespace() // this is working with newest Saxon 9.8.0-2 (not worked with 9.7.0-15
+ .ignoreComments() // this is not working even with newest Saxon 9.8.0-2
+ .withComparisonController(ComparisonControllers.Default)
+ .build();
+
+ assertTrue(d.toString(), !d.hasDifferences());
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class TestPatientFor327 extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "testCondition")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "testCondition", definedLocally = true, isModifier = false)
+ private List testConditions = null;
+
+ public List getConditions() {
+ return this.testConditions;
+ }
+
+ public void setCondition(List ref) {
+ this.testConditions = ref;
+ }
+ }
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/FooMessageHeader.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/FooMessageHeader.java
new file mode 100644
index 00000000000..84b8ceaf4ae
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/FooMessageHeader.java
@@ -0,0 +1,46 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.Block;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.Identifier;
+import org.hl7.fhir.dstu3.model.MessageHeader;
+import org.hl7.fhir.exceptions.FHIRException;
+
+@ResourceDef(name = "MessageHeader")
+public class FooMessageHeader extends MessageHeader {
+
+ private static final long serialVersionUID = 1L;
+
+ @Block()
+ public static class FooMessageSourceComponent extends MessageSourceComponent {
+
+ private static final long serialVersionUID = 1L;
+ @Child(name = "ext-messageheader-application-id", type = Identifier.class, modifier = true)
+ @Description(shortDefinition = "Message Header Application ID")
+ @Extension(url = "http://foo", definedLocally = false, isModifier = false)
+ private Identifier messageHeaderApplicationId;
+
+ /*
+ * Get messageHeaderApplicationId
+ */
+ public Identifier getMessageHeaderApplicationId() throws FHIRException {
+
+ if (messageHeaderApplicationId == null) {
+ messageHeaderApplicationId = new Identifier();
+ }
+ return messageHeaderApplicationId;
+ }
+
+ /*
+ * Set messageHeaderApplicationId
+ */
+ public void setmessageHeaderApplicationId(Identifier messageHeaderApplicationId) {
+ this.messageHeaderApplicationId = messageHeaderApplicationId;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/FooMessageHeaderWithExplicitField.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/FooMessageHeaderWithExplicitField.java
new file mode 100644
index 00000000000..97714273f47
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/FooMessageHeaderWithExplicitField.java
@@ -0,0 +1,57 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.Block;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.Identifier;
+import org.hl7.fhir.dstu3.model.MessageHeader;
+import org.hl7.fhir.exceptions.FHIRException;
+
+@ResourceDef(name = "FooMessageHeader")
+public class FooMessageHeaderWithExplicitField extends MessageHeader {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The source application from which this message originated.
+ */
+ @Child(name = "source", type = {}, order = Child.REPLACE_PARENT, min = 1, max = 1, modifier = false, summary = true)
+ @Description(shortDefinition = "Message Source Application", formalDefinition = "The source application from which this message originated.")
+ protected FooMessageSourceComponent source;
+
+ public void setSourceNew(FooMessageSourceComponent theSource) {
+ source = theSource;
+ }
+
+ @Block()
+ public static class FooMessageSourceComponent extends MessageSourceComponent {
+
+ private static final long serialVersionUID = 1L;
+ @Child(name = "ext-messageheader-application-id", type = Identifier.class, modifier = true)
+ @Description(shortDefinition = "Message Header Application ID")
+ @Extension(url = "http://foo", definedLocally = false, isModifier = false)
+ private Identifier messageHeaderApplicationId;
+
+ /*
+ * Get messageHeaderApplicationId
+ */
+ public Identifier getMessageHeaderApplicationId() throws FHIRException {
+
+ if (messageHeaderApplicationId == null) {
+ messageHeaderApplicationId = new Identifier();
+ }
+ return messageHeaderApplicationId;
+ }
+
+ /*
+ * Set messageHeaderApplicationId
+ */
+ public void setmessageHeaderApplicationId(Identifier messageHeaderApplicationId) {
+ this.messageHeaderApplicationId = messageHeaderApplicationId;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/MyPatientWithCustomUrlExtension.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/MyPatientWithCustomUrlExtension.java
new file mode 100644
index 00000000000..c4949c0e6aa
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/MyPatientWithCustomUrlExtension.java
@@ -0,0 +1,52 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.IdType;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.StringType;
+
+@ResourceDef()
+public class MyPatientWithCustomUrlExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "petName")
+ @Extension(url = "/petname", definedLocally = false, isModifier = false)
+ @Description(shortDefinition = "The name of the patient's favourite pet")
+ private StringType myPetName;
+
+ @Child(name = "customid")
+ @Extension(url = "/customid", definedLocally = false, isModifier = false)
+ @Description(shortDefinition = "The customid of the patient's ")
+ private IdType myCustomId;
+
+ public StringType getPetName() {
+ if (myPetName == null) {
+ myPetName = new StringType();
+ }
+ return myPetName;
+ }
+
+ public void setPetName(final StringType thePetName) {
+ myPetName = thePetName;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && getCustomId().isEmpty() && getPetName().isEmpty();
+ }
+
+ public IdType getCustomId() {
+ if (myCustomId == null) {
+ myCustomId = new IdType();
+ }
+ return myCustomId;
+ }
+
+ public void setCustomId(final IdType myCustomId) {
+ this.myCustomId = myCustomId;
+ }
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/MyPatientWithOneDeclaredEnumerationExtensionDstu3.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/MyPatientWithOneDeclaredEnumerationExtensionDstu3.java
new file mode 100644
index 00000000000..2be26671ce4
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/MyPatientWithOneDeclaredEnumerationExtensionDstu3.java
@@ -0,0 +1,66 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import java.util.List;
+import ca.uhn.fhir.model.api.annotation.Block;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import org.hl7.fhir.dstu3.model.Address.AddressUse;
+import org.hl7.fhir.dstu3.model.Enumeration;
+import org.hl7.fhir.dstu3.model.Identifier;
+import org.hl7.fhir.dstu3.model.Patient;
+
+@ResourceDef(name = "Patient")
+public class MyPatientWithOneDeclaredEnumerationExtensionDstu3 extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(order = 0, name = "foo")
+ @Extension(url = "urn:foo", definedLocally = true, isModifier = false)
+ private Enumeration myFoo;
+
+ /**
+ * A contact party (e.g. guardian, partner, friend) for the patient.
+ */
+ @Child(name = "contact", type = {}, order=Child.REPLACE_PARENT, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=false)
+ @Description(shortDefinition="A contact party (e.g. guardian, partner, friend) for the patient", formalDefinition="A contact party (e.g. guardian, partner, friend) for the patient." )
+ protected List contact;
+
+ public Enumeration getFoo() {
+ return myFoo;
+ }
+
+ public void setFoo(Enumeration theFoo) {
+ myFoo = theFoo;
+ }
+
+ @Block()
+ public static class MessageSourceComponent extends ContactComponent {
+
+ private static final long serialVersionUID = 1L;
+ @Child(name = "contact-eyecolour", type = { Identifier.class }, modifier = true)
+ @Description(shortDefinition = "Application ID")
+ @Extension(url = "http://foo.com/contact-eyecolour", definedLocally = false, isModifier = false)
+ private Identifier myEyeColour;
+
+ /*
+ * Get messageHeaderApplicationId
+ */
+ public Identifier getEyeColour() {
+ if (myEyeColour == null) {
+ myEyeColour = new Identifier();
+ }
+ return myEyeColour;
+ }
+
+ /*
+ * Set messageHeaderApplicationId
+ */
+ public void setEyeColour(Identifier messageHeaderApplicationId) {
+ this.myEyeColour = messageHeaderApplicationId;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/PatientProfileDstu3.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/PatientProfileDstu3.java
new file mode 100644
index 00000000000..f05e39f37c6
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/PatientProfileDstu3.java
@@ -0,0 +1,72 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Description;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.util.ElementUtil;
+import org.hl7.fhir.dstu3.model.CodeableConcept;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.Reference;
+
+@ResourceDef(name="Patient", profile = "http://hl7.org/fhir/StructureDefinition/Patient")
+public class PatientProfileDstu3 extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name="owner", min=0, max=1)
+ @Extension(url="http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#owningOrganization", definedLocally=false, isModifier=false)
+ @Description(shortDefinition="The organization that owns this animal")
+ private Reference owningOrganization;
+
+ public Reference getOwningOrganization() {
+ if (owningOrganization == null) {
+ owningOrganization = new Reference();
+ }
+ return owningOrganization;
+ }
+
+ public PatientProfileDstu3 setOwningOrganization(Reference owningOrganization) {
+ this.owningOrganization = owningOrganization;
+ return this;
+ }
+
+ @Child(name="colorPrimary", min=0, max=1)
+ @Extension(url="http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#animal-colorPrimary", definedLocally=false, isModifier=false)
+ @Description(shortDefinition="The animals primary color")
+ private CodeableConcept colorPrimary;
+
+ @Child(name="colorSecondary", min=0, max=1)
+ @Extension(url="http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#animal-colorSecondary", definedLocally=false, isModifier=false)
+ @Description(shortDefinition="The animals secondary color")
+ private CodeableConcept colorSecondary;
+
+ public CodeableConcept getColorPrimary() {
+ if (this.colorPrimary == null) {
+ return new CodeableConcept();
+ }
+ return colorPrimary;
+ }
+
+ public void setColorPrimary(CodeableConcept colorPrimary) {
+ this.colorPrimary = colorPrimary;
+ }
+
+ public CodeableConcept getColorSecondary() {
+ if (this.colorSecondary == null) {
+ return new CodeableConcept();
+ }
+ return colorSecondary;
+ }
+
+ public void setColorSecondary(CodeableConcept colorSecondary) {
+ this.colorSecondary = colorSecondary;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && ElementUtil.isEmpty(owningOrganization) && ElementUtil.isEmpty(colorPrimary)
+ && ElementUtil.isEmpty(colorSecondary) ;
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/PatientWithCustomCompositeExtension.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/PatientWithCustomCompositeExtension.java
new file mode 100644
index 00000000000..9521c2f96ff
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/PatientWithCustomCompositeExtension.java
@@ -0,0 +1,81 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.model.api.annotation.Block;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.util.ElementUtil;
+import org.hl7.fhir.dstu3.model.BackboneElement;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.StringType;
+
+@ResourceDef(name = "Patient")
+public class PatientWithCustomCompositeExtension extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * A custom extension
+ */
+ @Child(name = "foo")
+ @Extension(url="http://acme.org/fooParent", definedLocally = false, isModifier = false)
+ protected FooParentExtension fooParentExtension;
+
+ public FooParentExtension getFooParentExtension() {
+ return fooParentExtension;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && ElementUtil.isEmpty(fooParentExtension);
+ }
+
+ public void setFooParentExtension(FooParentExtension theFooParentExtension) {
+ fooParentExtension = theFooParentExtension;
+ }
+
+ @Block
+ public static class FooParentExtension extends BackboneElement {
+
+ private static final long serialVersionUID = 4522090347756045145L;
+
+ @Child(name = "childA")
+ @Extension(url = "http://acme.org/fooChildA", definedLocally = false, isModifier = false)
+ private StringType myChildA;
+
+ @Child(name = "childB")
+ @Extension(url = "http://acme.org/fooChildB", definedLocally = false, isModifier = false)
+ private StringType myChildB;
+
+ @Override
+ public FooParentExtension copy() {
+ FooParentExtension copy = new FooParentExtension();
+ copy.myChildA = myChildA;
+ copy.myChildB = myChildB;
+ return copy;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && ElementUtil.isEmpty(myChildA, myChildB);
+ }
+
+ public StringType getChildA() {
+ return myChildA;
+ }
+
+ public StringType getChildB() {
+ return myChildB;
+ }
+
+ public void setChildA(StringType theChildA) {
+ myChildA = theChildA;
+ }
+
+ public void setChildB(StringType theChildB) {
+ myChildB = theChildB;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java
new file mode 100644
index 00000000000..d0918aa628f
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/dstu3/ResourceValidatorDstu3FeatureTest.java
@@ -0,0 +1,410 @@
+package ca.uhn.fhir.tests.integration.karaf.dstu3;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.parser.DataFormatException;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.parser.StrictErrorHandler;
+import ca.uhn.fhir.validation.*;
+import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.Validate;
+import org.hamcrest.core.StringContains;
+import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
+import org.hl7.fhir.dstu3.context.IWorkerContext;
+import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
+import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
+import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
+import org.hl7.fhir.dstu3.model.*;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.utilities.validation.ValidationMessage;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_VALIDATION_DSTU3;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class ResourceValidatorDstu3FeatureTest {
+
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceValidatorDstu3FeatureTest.class);
+ private FhirContext ourCtx = FhirContext.forDstu3();
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ HAPI_FHIR_VALIDATION_DSTU3.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ WRAP.option(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+
+ private List logResultsAndReturnNonInformationalOnes(ValidationResult theOutput) {
+ List retVal = new ArrayList<>();
+
+ int index = 0;
+ for (SingleValidationMessage next : theOutput.getMessages()) {
+ ourLog.info("Result {}: {} - {} - {}",
+ new Object[]{index, next.getSeverity(), next.getLocationString(), next.getMessage()});
+ index++;
+
+ if (next.getSeverity() != ResultSeverityEnum.INFORMATION) {
+ retVal.add(next);
+ }
+ }
+
+ return retVal;
+ }
+
+ /**
+ * See issue #50
+ */
+ @Test
+ public void testOutOfBoundsDate() {
+ Patient p = new Patient();
+ p.setBirthDateElement(new DateType("2000-12-31"));
+
+ // Put in an invalid date
+ IParser parser = ourCtx.newXmlParser();
+ parser.setParserErrorHandler(new StrictErrorHandler());
+
+ String encoded = parser.setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
+ ourLog.info(encoded);
+
+ assertThat(encoded, StringContains.containsString("2000-15-31"));
+
+ ValidationResult result = ourCtx.newValidator().validateWithResult(encoded);
+ String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
+ ourLog.info(resultString);
+
+ assertEquals(2, ((OperationOutcome) result.toOperationOutcome()).getIssue().size());
+ assertThat(resultString, StringContains.containsString("cvc-pattern-valid"));
+
+ try {
+ parser.parseResource(encoded);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
+ }
+ }
+ @Test
+ public void testValidateCareTeamXsd() {
+ CareTeam careTeam = new CareTeam();
+ careTeam
+ .addParticipant()
+ .setMember(new Reference("http://example.com/Practitioner/1647bbb2-3b12-43cc-923c-a475f817e881"))
+ .setOnBehalfOf(new Reference("Organization/5859a28f-01e7-42d8-a8ba-48b31679a828"));
+
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+ String encoded = parser.encodeResourceToString(careTeam);
+
+ ourLog.info(encoded);
+
+ FhirValidator val = ourCtx.newValidator();
+
+ ValidationResult result = val.validateWithResult(encoded);
+ String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
+ ourLog.info(resultString);
+
+ assertThat(resultString, containsString("No issues detected during validation"));
+
+ }
+
+ @Test
+ public void testValidateCodeableConceptContainingOnlyBadCode() {
+ Patient p = new Patient();
+ p.getMaritalStatus().addCoding().setSystem("http://hl7.org/fhir/v3/MaritalStatus").setCode("FOO");
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ val.registerValidatorModule(new FhirInstanceValidator());
+
+ ValidationResult output = val.validateWithResult(p);
+ List all = logResultsAndReturnNonInformationalOnes(output);
+
+ assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/marital-status (http://hl7.org/fhir/ValueSet/marital-status, and a code should come from this value set unless it has no suitable code) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
+ assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity());
+ }
+
+ @Test
+ public void testValidateCodeableConceptContainingOnlyGoodCode() {
+ Patient p = new Patient();
+ p.getMaritalStatus().addCoding().setSystem("http://hl7.org/fhir/v3/MaritalStatus").setCode("M");
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ val.registerValidatorModule(new FhirInstanceValidator());
+
+ ValidationResult output = val.validateWithResult(p);
+ List all = logResultsAndReturnNonInformationalOnes(output);
+ assertEquals(0, all.size());
+ assertEquals(0, output.getMessages().size());
+ }
+
+ @Test
+ public void testValidateJsonNumericId() {
+ String input = "{\"resourceType\": \"Patient\",\n" +
+ " \"id\": 123,\n" +
+ " \"meta\": {\n" +
+ " \"versionId\": \"29\",\n" +
+ " \"lastUpdated\": \"2015-12-22T19:53:11.000Z\"\n" +
+ " },\n" +
+ " \"communication\": {\n" +
+ " \"language\": {\n" +
+ " \"coding\": [\n" +
+ " {\n" +
+ " \"system\": \"urn:ietf:bcp:47\",\n" +
+ " \"code\": \"hi\",\n" +
+ " \"display\": \"Hindi\",\n" +
+ " \"userSelected\": false\n" +
+ " }],\n" +
+ " \"text\": \"Hindi\"\n" +
+ " },\n" +
+ " \"preferred\": true\n" +
+ " }\n" +
+ "}";
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new FhirInstanceValidator());
+ ValidationResult output = val.validateWithResult(input);
+
+ OperationOutcome operationOutcome = (OperationOutcome) output.toOperationOutcome();
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
+// ourLog.info(encoded);
+
+ assertThat(encoded, containsString("Error parsing JSON: the primitive value must be a string"));
+
+ }
+
+ @Test
+ public void testValidateQuestionnaireWithCanonicalUrl() {
+ String input = "{\n" +
+ " \"resourceType\": \"Questionnaire\",\n" +
+ " \"url\": \"http://some.example.url\",\n" +
+ " \"status\": \"active\",\n" +
+ " \"subjectType\": [\n" +
+ " \"Patient\"\n" +
+ " ],\n" +
+ " \"item\": [\n" +
+ " {\n" +
+ " \"linkId\": \"example-question\",\n" +
+ " \"text\": \"Is the sky blue?\",\n" +
+ " \"type\": \"choice\",\n" +
+ " \"options\": {\n" +
+ " \"reference\": \"http://loinc.org/vs/LL3044-6\"\n" +
+ " }\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+
+ Questionnaire q = new Questionnaire();
+ q = ourCtx.newJsonParser().parseResource(Questionnaire.class, input);
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ val.registerValidatorModule(new FhirInstanceValidator());
+
+ ValidationResult result = val.validateWithResult(q);
+
+ OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
+ ourLog.info(encoded);
+
+ }
+
+ /**
+ * Make sure that the elements that appear in all resources (meta, language, extension, etc) all appear in the correct order
+ */
+ @Test
+ public void testValidateResourceWithResourceElements() {
+ TestPatientFor327 patient = new TestPatientFor327();
+ patient.setBirthDate(new Date());
+ patient.setId("123");
+ patient.getText().setDivAsString("FOO");
+ patient.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
+ patient.getLanguageElement().setValue("en");
+ patient.addExtension().setUrl("http://foo").setValue(new StringType("MOD"));
+ patient.getMeta().setLastUpdated(new Date());
+
+ List conditions = new ArrayList();
+ Condition condition = new Condition();
+ condition.getSubject().setReference("Patient/123");
+ condition.addBodySite().setText("BODY SITE");
+ condition.getCode().setText("CODE");
+ condition.setClinicalStatus(Condition.ConditionClinicalStatus.ACTIVE);
+ condition.setVerificationStatus(Condition.ConditionVerificationStatus.CONFIRMED);
+ conditions.add(new Reference(condition));
+ patient.setCondition(conditions);
+ patient.addIdentifier().setSystem("http://foo").setValue("123");
+
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
+ ourLog.info(encoded);
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ val.registerValidatorModule(new FhirInstanceValidator());
+
+ ValidationResult result = val.validateWithResult(encoded);
+
+ OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
+ String ooencoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
+ ourLog.info(ooencoded);
+
+ assertTrue(result.isSuccessful());
+
+ assertThat(ooencoded, containsString("Unknown extension http://foo"));
+ }
+
+ /**
+ * See https://groups.google.com/d/msgid/hapi-fhir/a266083f-6454-4cf0-a431-c6500f052bea%40googlegroups.com?utm_medium= email&utm_source=footer
+ */
+ @Test
+ public void testValidateWithExtensionsJson() {
+ PatientProfileDstu3 myPatient = new PatientProfileDstu3();
+ myPatient.setId("1");
+ myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey")));
+ myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-white")));
+ myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393"));
+ myPatient.addName().setFamily("FamilyName");
+ myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));
+
+ IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
+ String messageString = p.encodeResourceToString(myPatient);
+// ourLog.info(messageString);
+
+ String[] strings = {"meta",
+ "String Extension",
+ "Organization/2.25.79433498044103547197447759549862032393",
+ "furry-grey",
+ "furry-white",
+ "FamilyName"};
+ assertThat(messageString, stringContainsInOrder(
+ Arrays.asList(strings)
+ ));
+ String[] strings1 = {"extension",
+ "meta"};
+ assertThat(messageString, not(stringContainsInOrder(
+ Arrays.asList(strings1)
+ )));
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ val.registerValidatorModule(new FhirInstanceValidator());
+
+ ValidationResult result = val.validateWithResult(messageString);
+
+ OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
+ ourLog.info(encoded);
+
+ assertTrue(result.isSuccessful());
+
+ assertThat(messageString, containsString("valueReference"));
+ assertThat(messageString, not(containsString("valueResource")));
+ }
+
+ /**
+ * See https://groups.google.com/d/msgid/hapi-fhir/a266083f-6454-4cf0-a431-c6500f052bea%40googlegroups.com?utm_medium= email&utm_source=footer
+ */
+ @Test
+ public void testValidateWithExtensionsXml() {
+ PatientProfileDstu3 myPatient = new PatientProfileDstu3();
+ myPatient.setId("1");
+ myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey")));
+ myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-white")));
+ myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393"));
+ myPatient.addName().setFamily("FamilyName");
+ myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));
+
+ IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
+ String messageString = p.encodeResourceToString(myPatient);
+ ourLog.info(messageString);
+
+ //@formatter:off
+ String[] strings = {"meta",
+ "Organization/2.25.79433498044103547197447759549862032393",
+ "furry-grey",
+ "furry-white",
+ "String Extension",
+ "FamilyName"};
+ assertThat(messageString, stringContainsInOrder(
+ Arrays.asList(strings)
+ ));
+ String[] strings1 = {"extension",
+ "meta"};
+ assertThat(messageString, not(stringContainsInOrder(
+ Arrays.asList(strings1)
+ )));
+ assertThat(messageString, containsString("url=\"http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient#animal-colorSecondary\""));
+ assertThat(messageString, containsString("url=\"http://foo.com/example\""));
+ //@formatter:on
+
+ FhirValidator val = ourCtx.newValidator();
+ val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
+ val.registerValidatorModule(new SchematronBaseValidator(ourCtx));
+ val.registerValidatorModule(new FhirInstanceValidator());
+
+ ValidationResult result = val.validateWithResult(messageString);
+
+ OperationOutcome operationOutcome = (OperationOutcome) result.toOperationOutcome();
+ String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome);
+ ourLog.info(encoded);
+
+ assertTrue(result.isSuccessful());
+
+ assertThat(messageString, containsString("valueReference"));
+ assertThat(messageString, not(containsString("valueResource")));
+ }
+
+ @ResourceDef(name = "Patient")
+ public static class TestPatientFor327 extends Patient {
+
+ private static final long serialVersionUID = 1L;
+
+ @Child(name = "testCondition")
+ @ca.uhn.fhir.model.api.annotation.Extension(url = "testCondition", definedLocally = true, isModifier = false)
+ private List testConditions = null;
+
+ public List getConditions() {
+ return this.testConditions;
+ }
+
+ public void setCondition(List ref) {
+ this.testConditions = ref;
+ }
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/FhirInstanceValidatorR4Test.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/FhirInstanceValidatorR4Test.java
new file mode 100644
index 00000000000..1ae2468018f
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/FhirInstanceValidatorR4Test.java
@@ -0,0 +1,114 @@
+package ca.uhn.fhir.tests.integration.karaf.r4;
+
+import java.io.IOException;
+import java.util.List;
+import ca.uhn.fhir.context.FhirContext;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
+import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
+import org.hl7.fhir.r4.model.Base;
+import org.hl7.fhir.r4.model.BooleanType;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.StringType;
+import org.hl7.fhir.r4.model.StructureDefinition;
+import org.hl7.fhir.r4.utils.FHIRPathEngine;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_VALIDATION_R4;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class FhirInstanceValidatorR4Test {
+
+ private FhirContext ourCtx = FhirContext.forR4();
+ private FHIRPathEngine ourEngine = new FHIRPathEngine(new HapiWorkerContext(ourCtx, new DefaultProfileValidationSupport()));
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidatorR4Test.class);
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ HAPI_FHIR_VALIDATION_R4.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ WRAP.option(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ @Test
+ public void testAs() throws Exception {
+ Observation obs = new Observation();
+ obs.setValue(new StringType("FOO"));
+
+ List value = ourEngine.evaluate(obs, "Observation.value.as(String)");
+ assertEquals(1, value.size());
+ assertEquals("FOO", ((StringType)value.get(0)).getValue());
+ }
+
+ @Test
+ public void testExistsWithNoValue() throws FHIRException {
+ Patient patient = new Patient();
+ patient.setDeceased(new BooleanType());
+ List eval = ourEngine.evaluate(patient, "Patient.deceased.exists()");
+ ourLog.info(eval.toString());
+ assertFalse(((BooleanType)eval.get(0)).getValue());
+ }
+
+ @Test
+ public void testExistsWithValue() throws FHIRException {
+ Patient patient = new Patient();
+ patient.setDeceased(new BooleanType(false));
+ List eval = ourEngine.evaluate(patient, "Patient.deceased.exists()");
+ ourLog.info(eval.toString());
+ assertTrue(((BooleanType)eval.get(0)).getValue());
+ }
+
+ @Test
+ public void testConcatenation() throws FHIRException {
+ String exp = "Patient.name.family & '.'";
+
+ Patient p = new Patient();
+ p.addName().setFamily("TEST");
+ String result = ourEngine.evaluateToString(p, exp);
+ assertEquals("TEST.", result);
+ }
+
+ @Test
+ public void testConcatenationFunction() throws FHIRException {
+ String exp = "element.first().path.startsWith(%resource.type) and element.tail().all(path.startsWith(%resource.type&'.'))";
+
+ StructureDefinition sd = new StructureDefinition();
+ StructureDefinition.StructureDefinitionDifferentialComponent diff = sd.getDifferential();
+
+ diff.addElement().setPath("Patient.name");
+
+
+ Patient p = new Patient();
+ p.addName().setFamily("TEST");
+ List result = ourEngine.evaluate(null, p, diff, exp);
+ ourLog.info(result.toString());
+// assertEquals("TEST.", result);
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/R4JsonParserTest.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/R4JsonParserTest.java
new file mode 100644
index 00000000000..e5c617d5de2
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/R4JsonParserTest.java
@@ -0,0 +1,275 @@
+package ca.uhn.fhir.tests.integration.karaf.r4;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.parser.IParser;
+import com.google.common.collect.Sets;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Extension;
+import org.hl7.fhir.r4.model.Medication;
+import org.hl7.fhir.r4.model.MedicationDispense;
+import org.hl7.fhir.r4.model.MedicationRequest;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.StringType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_R4;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class R4JsonParserTest {
+
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(R4JsonParserTest.class);
+ private FhirContext ourCtx = FhirContext.forR4();
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_R4.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+
+ private Bundle createBundleWithPatient() {
+ Bundle b = new Bundle();
+ b.setId("BUNDLEID");
+ b.getMeta().addProfile("http://FOO");
+
+ Patient p = new Patient();
+ p.setId("PATIENTID");
+ p.getMeta().addProfile("http://BAR");
+ p.addName().addGiven("GIVEN");
+ b.addEntry().setResource(p);
+ return b;
+ }
+
+ /**
+ * See #814
+ */
+ @Test
+ public void testDuplicateContainedResourcesNotOutputtedTwice() {
+ MedicationDispense md = new MedicationDispense();
+
+ MedicationRequest mr = new MedicationRequest();
+ md.addAuthorizingPrescription().setResource(mr);
+
+ Medication med = new Medication();
+ md.setMedication(new Reference(med));
+ mr.setMedication(new Reference(med));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(md);
+ ourLog.info(encoded);
+
+ int idx = encoded.indexOf("\"Medication\"");
+ assertNotEquals(-1, idx);
+
+ idx = encoded.indexOf("\"Medication\"", idx + 1);
+ assertEquals(-1, idx);
+
+ }
+
+ /**
+ * See #814
+ */
+ @Test
+ public void testDuplicateContainedResourcesNotOutputtedTwiceWithManualIds() {
+ MedicationDispense md = new MedicationDispense();
+
+ MedicationRequest mr = new MedicationRequest();
+ mr.setId("#MR");
+ md.addAuthorizingPrescription().setResource(mr);
+
+ Medication med = new Medication();
+ med.setId("#MED");
+ md.setMedication(new Reference(med));
+ mr.setMedication(new Reference(med));
+
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(md);
+ ourLog.info(encoded);
+
+ int idx = encoded.indexOf("\"Medication\"");
+ assertNotEquals(-1, idx);
+
+ idx = encoded.indexOf("\"Medication\"", idx + 1);
+ assertEquals(-1, idx);
+
+ }
+
+ @Test
+ public void testExcludeNothing() {
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+ Set excludes = new HashSet<>();
+// excludes.add("*.id");
+ parser.setDontEncodeElements(excludes);
+
+ Bundle b = createBundleWithPatient();
+
+ String encoded = parser.encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("BUNDLEID"));
+ assertThat(encoded, containsString("http://FOO"));
+ assertThat(encoded, containsString("PATIENTID"));
+ assertThat(encoded, containsString("http://BAR"));
+ assertThat(encoded, containsString("GIVEN"));
+
+ b = parser.parseResource(Bundle.class, encoded);
+
+ assertEquals("BUNDLEID", b.getIdElement().getIdPart());
+ assertEquals("Patient/PATIENTID", ((Patient) b.getEntry().get(0).getResource()).getId());
+ assertEquals("GIVEN", ((Patient) b.getEntry().get(0).getResource()).getNameFirstRep().getGivenAsSingleString());
+ }
+
+ @Test
+ public void testExcludeRootStuff() {
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+ Set excludes = new HashSet<>();
+ excludes.add("id");
+ excludes.add("meta");
+ parser.setDontEncodeElements(excludes);
+
+ Bundle b = createBundleWithPatient();
+
+ String encoded = parser.encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, not(containsString("BUNDLEID")));
+ assertThat(encoded, not(containsString("http://FOO")));
+ assertThat(encoded, (containsString("PATIENTID")));
+ assertThat(encoded, (containsString("http://BAR")));
+ assertThat(encoded, containsString("GIVEN"));
+
+ b = parser.parseResource(Bundle.class, encoded);
+
+ assertNotEquals("BUNDLEID", b.getIdElement().getIdPart());
+ assertEquals("Patient/PATIENTID", ((Patient) b.getEntry().get(0).getResource()).getId());
+ assertEquals("GIVEN", ((Patient) b.getEntry().get(0).getResource()).getNameFirstRep().getGivenAsSingleString());
+ }
+
+ @Test
+ public void testExcludeStarDotStuff() {
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+ Set excludes = new HashSet<>();
+ excludes.add("*.id");
+ excludes.add("*.meta");
+ parser.setDontEncodeElements(excludes);
+
+ Bundle b = createBundleWithPatient();
+
+ String encoded = parser.encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, not(containsString("BUNDLEID")));
+ assertThat(encoded, not(containsString("http://FOO")));
+ assertThat(encoded, not(containsString("PATIENTID")));
+ assertThat(encoded, not(containsString("http://BAR")));
+ assertThat(encoded, containsString("GIVEN"));
+
+ b = parser.parseResource(Bundle.class, encoded);
+
+ assertNotEquals("BUNDLEID", b.getIdElement().getIdPart());
+ assertNotEquals("Patient/PATIENTID", ((Patient) b.getEntry().get(0).getResource()).getId());
+ assertEquals("GIVEN", ((Patient) b.getEntry().get(0).getResource()).getNameFirstRep().getGivenAsSingleString());
+ }
+
+ /**
+ * Test that long JSON strings don't get broken up
+ */
+ @Test
+ public void testNoBreakInLongString() {
+ String longString = StringUtils.leftPad("", 100000, 'A');
+
+ Patient p = new Patient();
+ p.addName().setFamily(longString);
+ String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
+
+ assertThat(encoded, containsString(longString));
+ }
+
+ @Test
+ public void testParseAndEncodeExtensionWithValueWithExtension() {
+ String input = "{\n" +
+ " \"resourceType\": \"Patient\",\n" +
+ " \"extension\": [\n" +
+ " {\n" +
+ " \"url\": \"https://purl.org/elab/fhir/network/StructureDefinition/1/BirthWeight\",\n" +
+ " \"_valueDecimal\": {\n" +
+ " \"extension\": [\n" +
+ " {\n" +
+ " \"url\": \"http://www.hl7.org/fhir/extension-data-absent-reason.html\",\n" +
+ " \"valueCoding\": {\n" +
+ " \"system\": \"http://hl7.org/fhir/ValueSet/birthweight\",\n" +
+ " \"code\": \"Underweight\",\n" +
+ " \"userSelected\": false\n" +
+ " }\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"identifier\": [\n" +
+ " {\n" +
+ " \"system\": \"https://purl.org/elab/fhir/network/StructureDefinition/1/EuroPrevallStudySubjects\",\n" +
+ " \"value\": \"1\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"gender\": \"female\"\n" +
+ "}";
+
+ IParser jsonParser = ourCtx.newJsonParser();
+ IParser xmlParser = ourCtx.newXmlParser();
+ jsonParser.setDontEncodeElements(Sets.newHashSet("id", "meta"));
+ xmlParser.setDontEncodeElements(Sets.newHashSet("id", "meta"));
+
+ Patient parsed = jsonParser.parseResource(Patient.class, input);
+
+ ourLog.info(jsonParser.setPrettyPrint(true).encodeResourceToString(parsed));
+ assertThat(xmlParser.encodeResourceToString(parsed), containsString("Underweight"));
+ assertThat(jsonParser.encodeResourceToString(parsed), containsString("Underweight"));
+
+ }
+
+ @Test
+ public void testParseExtensionOnPrimitive() throws IOException {
+ String input = IOUtils.toString(R4JsonParserTest.class.getResourceAsStream("/extension-on-line.txt"));
+ IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
+ Patient pt = parser.parseResource(Patient.class, input);
+
+ StringType line0 = pt.getAddressFirstRep().getLine().get(0);
+ assertEquals("535 Sheppard Avenue West, Unit 1907", line0.getValue());
+ Extension houseNumberExt = line0.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-houseNumber");
+ assertEquals("535", ((StringType) houseNumberExt.getValue()).getValue());
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/R4XmlParserTest.java b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/R4XmlParserTest.java
new file mode 100644
index 00000000000..67b75f1148f
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/java/ca/uhn/fhir/tests/integration/karaf/r4/R4XmlParserTest.java
@@ -0,0 +1,195 @@
+package ca.uhn.fhir.tests.integration.karaf.r4;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.parser.IParser;
+import com.google.common.collect.Sets;
+import org.hamcrest.core.IsNot;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Patient;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.HAPI_FHIR_R4;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.KARAF;
+import static ca.uhn.fhir.tests.integration.karaf.PaxExamOptions.WRAP;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.debugConfiguration;
+
+
+/**
+ * Useful docs about this test: https://ops4j1.jira.com/wiki/display/paxexam/FAQ
+ */
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class R4XmlParserTest {
+
+ private final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(R4XmlParserTest.class);
+ private FhirContext ourCtx = FhirContext.forR4();
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ KARAF.option(),
+ WRAP.option(),
+ HAPI_FHIR_R4.option(),
+ mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+ when(false)
+ .useOptions(
+ debugConfiguration("5005", true))
+ );
+ }
+ private Bundle createBundleWithPatient() {
+ Bundle b = new Bundle();
+ b.setId("BUNDLEID");
+ b.getMeta().addProfile("http://FOO");
+
+ Patient p = new Patient();
+ p.setId("PATIENTID");
+ p.getMeta().addProfile("http://BAR");
+ p.addName().addGiven("GIVEN");
+ b.addEntry().setResource(p);
+ return b;
+ }
+
+ @Test
+ public void testParseAndEncodeXmlNumericEntity() {
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ Patient p = ourCtx.newXmlParser().parseResource(Patient.class, input);
+ assertEquals("A \n B", p.getNameFirstRep().getFamily());
+
+ String output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
+ ourLog.info(output);
+
+ }
+
+ @Test
+ public void testExcludeNothing() {
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+ Set excludes = new HashSet<>();
+// excludes.add("*.id");
+ parser.setDontEncodeElements(excludes);
+
+ Bundle b = createBundleWithPatient();
+
+ String encoded = parser.encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, containsString("BUNDLEID"));
+ assertThat(encoded, containsString("http://FOO"));
+ assertThat(encoded, containsString("PATIENTID"));
+ assertThat(encoded, containsString("http://BAR"));
+ assertThat(encoded, containsString("GIVEN"));
+
+ b = parser.parseResource(Bundle.class, encoded);
+
+ assertEquals("BUNDLEID", b.getIdElement().getIdPart());
+ assertEquals("Patient/PATIENTID", ((Patient) b.getEntry().get(0).getResource()).getId());
+ assertEquals("GIVEN", ((Patient) b.getEntry().get(0).getResource()).getNameFirstRep().getGivenAsSingleString());
+ }
+
+ @Test
+ public void testExcludeRootStuff() {
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+ Set excludes = new HashSet<>();
+ excludes.add("id");
+ excludes.add("meta");
+ parser.setDontEncodeElements(excludes);
+
+ Bundle b = createBundleWithPatient();
+
+ String encoded = parser.encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, IsNot.not(containsString("BUNDLEID")));
+ assertThat(encoded, IsNot.not(containsString("http://FOO")));
+ assertThat(encoded, (containsString("PATIENTID")));
+ assertThat(encoded, (containsString("http://BAR")));
+ assertThat(encoded, containsString("GIVEN"));
+
+ b = parser.parseResource(Bundle.class, encoded);
+
+ assertNotEquals("BUNDLEID", b.getIdElement().getIdPart());
+ assertEquals("Patient/PATIENTID", ((Patient) b.getEntry().get(0).getResource()).getId());
+ assertEquals("GIVEN", ((Patient) b.getEntry().get(0).getResource()).getNameFirstRep().getGivenAsSingleString());
+ }
+
+ @Test
+ public void testExcludeStarDotStuff() {
+ IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
+ Set excludes = new HashSet<>();
+ excludes.add("*.id");
+ excludes.add("*.meta");
+ parser.setDontEncodeElements(excludes);
+
+ Bundle b = createBundleWithPatient();
+
+ String encoded = parser.encodeResourceToString(b);
+ ourLog.info(encoded);
+
+ assertThat(encoded, IsNot.not(containsString("BUNDLEID")));
+ assertThat(encoded, IsNot.not(containsString("http://FOO")));
+ assertThat(encoded, IsNot.not(containsString("PATIENTID")));
+ assertThat(encoded, IsNot.not(containsString("http://BAR")));
+ assertThat(encoded, containsString("GIVEN"));
+
+ b = parser.parseResource(Bundle.class, encoded);
+
+ assertNotEquals("BUNDLEID", b.getIdElement().getIdPart());
+ assertNotEquals("Patient/PATIENTID", ((Patient) b.getEntry().get(0).getResource()).getId());
+ assertEquals("GIVEN", ((Patient) b.getEntry().get(0).getResource()).getNameFirstRep().getGivenAsSingleString());
+ }
+
+ @Test
+ public void testParseAndEncodeExtensionWithValueWithExtension() {
+ String input = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "
\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ IParser xmlParser = ourCtx.newXmlParser();
+ IParser jsonParser = ourCtx.newJsonParser();
+ jsonParser.setDontEncodeElements(Sets.newHashSet("id", "meta"));
+ xmlParser.setDontEncodeElements(Sets.newHashSet("id", "meta"));
+
+ Patient parsed = xmlParser.parseResource(Patient.class, input);
+
+ ourLog.info(jsonParser.setPrettyPrint(true).encodeResourceToString(parsed));
+ assertThat(xmlParser.encodeResourceToString(parsed), containsString("Underweight"));
+ assertThat(jsonParser.encodeResourceToString(parsed), containsString("Underweight"));
+
+ }
+
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/bundle-example.json b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle-example.json
new file mode 100644
index 00000000000..24557e54b65
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle-example.json
@@ -0,0 +1,53 @@
+{
+ "resourceType": "Bundle",
+ "id": "example",
+ "meta": {
+ "versionId": "1",
+ "lastUpdated": "2014-08-18T01:43:30Z"
+ },
+ "type": "searchset",
+ "total": 3,
+ "link": [
+ {
+ "relation": "next",
+ "url": "https://example.com/base/MedicationOrder?patient=347&searchId=ff15fd40-ff71-4b48-b366-09c706bed9d0&page=2"
+ },
+ {
+ "relation": "self",
+ "url": "https://example.com/base/MedicationOrder?patient=347&_include=MedicationOrder.medication"
+ }
+ ],
+ "entry": [
+ {
+ "fullUrl": "http://example.com/base/MedicationOrder/3123/_history/1",
+ "resource": {
+ "resourceType": "MedicationOrder",
+ "id": "3123",
+ "meta" : {
+ "versionId" : "1",
+ "lastUpdated" : "2014-08-16T05:31:17Z"
+ },
+ "patient": {
+ "reference": "Patient/347"
+ },
+ "medicationReference": {
+ "reference": "Medication/example"
+ }
+ },
+ "search": {
+ "mode": "match",
+ "score": 1
+ }
+ },
+ {
+ "fullUrl": "http://example.com/base/Medication/example",
+ "resource": {
+ "resourceType": "Medication",
+ "id": "example"
+ },
+ "search": {
+ "mode": "include"
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_orion.xml b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_orion.xml
new file mode 100644
index 00000000000..37a83687b99
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_orion.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_ref_by_uuid_544.xml b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_ref_by_uuid_544.xml
new file mode 100644
index 00000000000..8fe8320c489
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_ref_by_uuid_544.xml
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_contained_with_no_id.xml b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_contained_with_no_id.xml
new file mode 100644
index 00000000000..cd189a0838e
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_contained_with_no_id.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_invalid_contained_ref.xml b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_invalid_contained_ref.xml
new file mode 100644
index 00000000000..d10ece0f0b4
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_invalid_contained_ref.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_woven_obs.xml b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_woven_obs.xml
new file mode 100644
index 00000000000..a7d0cca0016
--- /dev/null
+++ b/hapi-fhir-karaf-integration-tests/src/test/resources/bundle_with_woven_obs.xml
@@ -0,0 +1,317 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hapi-fhir-karaf-integration-tests/src/test/resources/conformance.json.gz b/hapi-fhir-karaf-integration-tests/src/test/resources/conformance.json.gz
new file mode 100644
index 0000000000000000000000000000000000000000..9f5ca59e23715949f366c9b61fe80fce5daba832
GIT binary patch
literal 40983
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