# 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.