# Parser Configuration vs `FhirContext` Configuration ## Question Can parser settings like "override resource IDs with bundle entry full URLs" be configured on `FhirContext`, or is this entirely a parser-level concern? ## Answer **Both!** `FhirContext` can set **default** parser options via `ParserOptions`, and individual parsers can override these defaults. ## Why ### `FhirContext` is a Factory `FhirContext` is essentially a factory/configuration object for creating parsers. It provides: - FHIR version information (R4, R5, DSTU3, etc.) - Parser error handlers (lenient vs strict) - Model class factory - Validation support ### Parsers Have Their Own Configuration `IParser` instances (created via `fhirContext.newXmlParser()` or `fhirContext.newJsonParser()`) have their own configuration methods that are **independent** of `FhirContext`: ```java // Parser-level settings (NOT FhirContext settings) IParser parser = fhirContext.newXmlParser(); parser.setPrettyPrint(true); parser.setOverrideResourceIdWithBundleEntryFullUrl(true); parser.setBundleEntryFullUrlMode(BundleEntryFullUrlModeEnum.FULL_URL_NULL_IF_ROOT_RESOURCE); parser.setParserOptions(...); ``` ## Parser Configuration Examples ### Override Resource ID with Bundle Entry Full URL ```java // Get FhirContext from controller service IFhirContextService service = context.getProperty( FHIR_CONTEXT_SERVICE_PROPERTY ) .asControllerService( IFhirContextService.class ); FhirContext fhirContext = service.getFhirContext(); // Create parser and configure it IParser parser = fhirContext.newXmlParser(); parser.setOverrideResourceIdWithBundleEntryFullUrl(true); // Parser-level setting! // Use the configured parser Bundle bundle = parser.parseResource(Bundle.class, inputStream); ``` ### Other Parser-Level Settings ```java IParser parser = fhirContext.newXmlParser(); // Parser-specific configurations parser.setPrettyPrint(true); parser.setOverrideResourceIdWithBundleEntryFullUrl(true); parser.setBundleEntryFullUrlMode(BundleEntryFullUrlModeEnum.FULL_URL_NULL_IF_ROOT_RESOURCE); parser.setStripVersionsFromReferences(false); parser.setSummaryMode(false); parser.setDontEncodeElements(Collections.singleton("Patient.meta")); parser.setEncodeElements(Collections.singleton("Patient.name")); ``` ## What CAN Be Configured on `FhirContext` `FhirContext` supports these configuration options that affect parsers: ```java FhirContext context = FhirContext.forR4(); // 1. ParserErrorHandler - Error handling behavior (inherited by all parsers) context.setParserErrorHandler(new LenientErrorHandler(false).setErrorOnInvalidValue(false)); // All parsers created from this context will use lenient error handling // 2. ParserOptions - Default parser behavior settings (inherited by all parsers) ParserOptions options = context.getParserOptions(); options.setOverrideResourceIdWithBundleEntryFullUrl(false); // Default for all parsers options.setStripVersionsFromReferences(true); context.setParserOptions(options); // All parsers created from this context will inherit these defaults // 3. Other FhirContext-level settings context.setDefaultTypeForProfile("http://example.org/profile", Patient.class); context.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); ``` **Important:** - **ParserErrorHandler** set on `FhirContext` is used by all parsers created from that context - **`ParserOptions`** set on `FhirContext` provide **defaults** for all parsers created from that context - Individual parsers can still override `ParserOptions` defaults (but not the error handler) ## Implications for a `FhirContext` controller A `FhirContext` controller could configure settings that affect all parsers: ### 1. ParserErrorHandler ```java if( lenientParsing ) { IParserErrorHandler lenient = new LenientErrorHandler( false ).setErrorOnInvalidValue( false ); context.setParserErrorHandler( lenient ); } ``` **All parsers** created from this `FhirContext` will use the lenient error handler. ### 2. `ParserOptions` You could also configure default `ParserOptions`: ```java ParserOptions options = context.getParserOptions(); options.setOverrideResourceIdWithBundleEntryFullUrl(false); // Set default options.setStripVersionsFromReferences(true); context.setParserOptions(options); ``` **However:** - These are **defaults** - individual parsers can still override them - Some settings like `setPrettyPrint()` are **not** part of `ParserOptions` (they're parser-specific) - Processors can still configure individual parsers differently if needed **Best practice:** - Configure ParserErrorHandler in a `FhirContext` controller (applies to all parsers, cannot be overridden) - Configure `ParserOptions` defaults in `FhirContext` controller (applies to all parsers, can be overridden per parser) - Allow processors to override `ParserOptions` when needed ## Best Practice Pattern ```java public class MyProcessor extends AbstractProcessor { @Override public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException { // Get shared FhirContext from controller service IFhirContextService service = context.getProperty( FHIR_CONTEXT_SERVICE_PROPERTY ) .asControllerService( IFhirContextService.class ); FhirContext fhirContext = service.getFhirContext(); // Create parser and configure it for THIS processor's needs IParser parser = fhirContext.newXmlParser(); // Configure parser-level settings (processor-specific) parser.setOverrideResourceIdWithBundleEntryFullUrl(true); parser.setPrettyPrint(contextProperties.prettyPrint); // ... other parser settings ... // Use the configured parser Bundle bundle = parser.parseResource(Bundle.class, inputStream); } } ``` ## Why Parsers Are Not Thread-Safe Note: Parsers are **not thread-safe**, which is why: 1. You create a new parser instance for each use (or per thread) 2. You configure parser settings on each instance 3. You don't share parser instances across threads This is different from `FhirContext`, which **is** thread-safe and can be shared. ## Summary - ✅ **`FhirContext`**: Shared, thread-safe, provides FHIR version, error handling, and **default `ParserOptions`** - ✅ **`ParserOptions`**: Can be set on `FhirContext` to provide defaults for all parsers - ✅ **`IParser`**: Created from `FhirContext`, inherits `ParserOptions` defaults, can override them - ✅ **Individual parser settings**: Can override `FhirContext` defaults when needed - ⚠️ **Some settings**: Like `setPrettyPrint()` are parser-specific and not part of `ParserOptions` ## The Relationship ### Example 1: `ParserOptions` (can be overridden) ```java // 1. FhirContext sets default ParserOptions FhirContext context = FhirContext.forR4(); ParserOptions options = context.getParserOptions(); options.setOverrideResourceIdWithBundleEntryFullUrl(false); // Default context.setParserOptions(options); // 2. Parsers created from this context inherit the default IParser parser1 = context.newXmlParser(); // parser1 will have overrideResourceIdWithBundleEntryFullUrl = false (not the default) // 3. Individual parsers can override the default IParser parser2 = context.newXmlParser(); parser2.setOverrideResourceIdWithBundleEntryFullUrl(true); // override the overridden default // parser2 will have overrideResourceIdWithBundleEntryFullUrl = true (overridden) ``` ### Example 2: ParserErrorHandler (inherited, not overridden) ```java // 1. FhirContext sets ParserErrorHandler FhirContext context = FhirContext.forR4(); IParserErrorHandler lenient = new LenientErrorHandler(false).setErrorOnInvalidValue(false); context.setParserErrorHandler(lenient); // 2. All parsers created from this context use the same error handler IParser parser1 = context.newXmlParser(); IParser parser2 = context.newJsonParser(); // Both parser1 and parser2 will use the lenient error handler // (Note: There's no way to override the error handler on individual parsers) ``` ### Summary A `FhirContext` controller could configure: - **ParserErrorHandler**: Applied to all parsers (cannot be overridden per parser) - **`ParserOptions`**: Defaults applied to all parsers (can be overridden per parser) A `FhirContext` controller could provide consistent defaults across all processors, while still allowing individual processors to override `ParserOptions` when needed.