NEWFree ROI Calculators — quantify what prior auth and siloed data are costing your organization.Prior Auth ROI Siloed Data ROI
HL7 v2Data Type8 min read

HL7 Varies Data Type: Variant Field Resolved at Parse Time

The Varies data type — sometimes called "Variant" in implementer documentation — is HL7 v2's polymorphic placeholder. A field declared as Varies has no fixed structure at schema time; its actual data type, component count, and parsing rules are determined at parse time by reading a sibling discriminator field elsewhere in the same segment. The canonical case is OBX-5 Observation Value, whose runtime type is set by OBX-2 Value Type: when OBX-2 holds NM, OBX-5 is a single numeric; when OBX-2 holds CWE, OBX-5 is a 22-component CWE composite; when OBX-2 holds ED, OBX-5 is an embedded encoded document. The receiver must read OBX-2 first or it cannot parse OBX-5 at all.

Purpose

Varies exists because the alternative — defining a separate field for every possible value type — would explode the segment. OBSERVATION segments in particular carry data whose shape genuinely varies by observation: a glucose result is a number, a pathology impression is free text, a coded finding is a CWE, a chest X-ray is an embedded ED document. HL7 v2 chose to fold all of those into a single OBX-5 field and to drive the parser from OBX-2.

The trade-off is that Varies fields are not parseable in isolation. Every engine — at every hop — has to implement the discriminator lookup, or it loses the value. Validating a Varies field against a fixed schema is a category error: there is no fixed schema. There is only "what OBX-2 said this time."

Resolution rules

Unlike every other HL7 v2 data type, Varies has no static component table. Instead it has a dispatch rule. The receiver applies the rule in three steps.

  1. Locate the discriminator. Each Varies field has a specified sibling that names the active type. For OBX-5 it is OBX-2 Value Type. For CSU-6 (Channel Sensitivity / Units) in waveform / clinical-study messages it is CSU-3 / CSU-4. The discriminator is always a 2-3 character HL7 type abbreviation drawn from HL70125 (Value Type), e.g. ST, NM, CWE, CE, ED, RP, NA, SN, TX, FT, DT, TM, DTM.
  2. Resolve the type. Map the discriminator value to a concrete HL7 v2 data type class. NM → Numeric, CWE → Coded With Exceptions (22 components), ED → Encoded Data (5 components), SN → Structured Numeric (4 components), etc.
  3. Dispatch to that parser. Parse the Varies field's raw bytes using the rules of the resolved type — its component separator semantics, its sub-component rules, its length and precision rules. Repetitions inside the Varies field (~) repeat the same resolved type; OBX-2 is not re-read per repetition.

When OBX-2 is absent or holds an unknown code, the field cannot be parsed. The conventional safe behavior is to surface OBX-5 as ST (string) and emit a warning; silently dropping the field is a data-loss bug.

Most-used resolutions

  • OBX-2 = NM — OBX-5 parses as a single numeric value, optionally signed.
  • OBX-2 = ST — OBX-5 parses as a single string with HL7 v2 escape sequences honored.
  • OBX-2 = CWE — OBX-5 parses as a full 22-component CWE composite. Used for coded observations (laboratory results from atomic LOINC panels, snomed-coded findings).
  • OBX-2 = ED — OBX-5 parses as a 5-component ED with embedded base64 payload. Used for embedded PDFs, images, and waveforms.
  • OBX-2 = SN — OBX-5 parses as a 4-component Structured Numeric (<comparator>^<num1>^<separator>^<num2>), used for ranges and tolerances.
  • OBX-2 = TX / FT — OBX-5 parses as free text or formatted text, often with embedded newlines via the .br escape.

Where it's used

Varies is rare across the v2 catalog and concentrated in segments where the payload is genuinely polymorphic.

  • OBX-5 Observation Value (the dominant case)
  • OBX-19 Performing Organization Address sub-fields in profiles that re-use OBX value typing
  • CSU-6 Channel Sensitivity / Units in clinical-study and waveform messages
  • A handful of Z-segments that mirror the OBX shape

Outside these, fields with a polymorphic feel are usually CWE rather than Varies — CWE handles "any code from any system" without runtime dispatch.

Version differences

  • v2.1 / v2.2 — Informal precursor: OBX-5's type was already discriminated by OBX-2, but Varies was not a named data type.
  • v2.3 — Varies formalized as a named meta-type. The HAPI v2.3 structure package introduces the Varies class.
  • v2.4 / v2.5 / v2.6 / v2.7 — Discriminator semantics unchanged. The set of legal values in HL70125 grows (CWE arrives in v2.5, CNE in v2.6, etc.), expanding the universe of resolvable types but not the dispatch rule itself.
  • v2.8 / v2.8.1 / v2.8.2 — Stable per HAPI v2.8.1. ca.uhn.hl7v2.model.v281.datatype.Varies exposes the same dynamic-typing API: a getData() accessor that returns whatever concrete type was resolved at parse time.

Common mistakes

  • Validating Varies against a fixed schema. There is no fixed schema. Schema validators that assert "OBX-5 has N components" are wrong by construction.
  • Ignoring OBX-2. Code paths that grab OBX-5.getValue() and treat it as a string lose all structure when OBX-2 said CWE or ED.
  • Re-reading OBX-2 per repetition. OBX-2 governs the entire OBX-5 field, including all ~-separated repetitions; treating each repetition as independently typed produces garbage.
  • Sending OBX-5 without OBX-2. Even when the value is "obviously" a string, omitting OBX-2 leaves the receiver guessing. Default to ST if nothing else fits, but always populate OBX-2.
  • Mapping Varies straight to FHIR without dispatch. Observation.value[x] in FHIR is its own polymorphic field; the v2 → FHIR engine must resolve OBX-2 first, then choose valueQuantity vs valueCodeableConcept vs valueString vs valueAttachment accordingly.

Examples

OBX-2 = NM — OBX-5 is a single numeric (glucose result):

OBX|1|NM|GLU^Glucose^LN||5.5|mmol/L^^UCUM|3.9-5.6|N|||F

OBX-2 = CWE — OBX-5 is a coded result (positive culture finding):

OBX|2|CWE|600-7^Bacteria identified^LN||positive^Positive^L|||A|||F

OBX-2 = ST — OBX-5 is a free string (specimen comment):

OBX|3|ST|COMMENT^Lab comment^L||Specimen hemolyzed; recollect if clinically indicated.||||||F

OBX-2 = SN — OBX-5 is a Structured Numeric (titre range):

OBX|4|SN|TITRE^Antibody titre^L||^1^:^64||1:8-1:32|H|||F

OBX-2 = ED — OBX-5 is an embedded encoded document (PDF report stub):

OBX|5|ED|RPT^Report^L||^application^pdf^Base64^JVBERi0xLjQK...||||||F

In context — single OBR with mixed-type OBX repetitions, each with its own OBX-2:

OBR|1|||CBC^Complete Blood Count^L|||20260610080000
OBX|1|NM|WBC^WBC^LN||6.2|10*9/L^^UCUM|4.0-11.0|N|||F
OBX|2|NM|HGB^Hemoglobin^LN||135|g/L^^UCUM|130-170|N|||F
OBX|3|CWE|MORPH^RBC morphology^L||N^Normal^L|||N|||F
OBX|4|TX|COMMENT^Comment^L||Differential reviewed; no abnormal cells seen.||||||F

Common pitfall — OBX-5 sent without OBX-2:

OBX|1||GLU^Glucose^LN||5.5|mmol/L|3.9-5.6|N|||F

The receiver has no basis to pick a parser. Conservative engines treat OBX-5 as ST and warn; strict engines reject the segment.

FHIR mapping

The HL7 v2-to-FHIR Implementation Guide does not publish a per-datatype ConceptMap for Varies, because Varies has no fixed structure to map. Instead, the IG defines the dispatch indirectly: every concrete data type that can land in a Varies field has its own ConceptMap, and the v2 → FHIR engine selects the right one at runtime.

In practice, for the OBX-5 case the engine maps the resolved type onto an Observation.value[x] choice:

Resolved OBX-2 typeFHIR target
NMObservation.valueQuantity (combined with OBX-6 units)
SNObservation.valueQuantity or Observation.valueRange
ST / TX / FTObservation.valueString
CWE / CEObservation.valueCodeableConcept
EDObservation.valueAttachment
DT / DTMObservation.valueDateTime
TMObservation.valueTime

The same dispatch logic applies to CSU and any custom Varies use. The mapping is always indirect through the discriminator.

Engine considerations

  • Read order matters. Parsers must read OBX-2 before OBX-5 in the same segment. Streaming parsers that emit fields in order need a one-segment lookahead or a two-pass pass over each OBR.
  • Type registry. Engines benefit from a centralized table mapping HL70125 codes to parser classes. Adding a new value type then becomes a single-table edit instead of a code change at every call site.
  • Repetition semantics. A ~-separated repetition list in OBX-5 inherits OBX-2's type for every element. The repetition separator never re-triggers discriminator lookup.
  • Round-trip fidelity. When re-emitting an OBX, keep OBX-2 and OBX-5 paired. Touching OBX-5 without re-asserting OBX-2 risks drift if a downstream system defaults differently.
  • Validation. Validate the resolved type, not Varies itself. "OBX-5 must have 22 components" is wrong; "OBX-5 must have 22 components when OBX-2 = CWE" is right.
  • HAPI typing. ca.uhn.hl7v2.model.v281.datatype.Varies.getData() returns a Type whose concrete class reflects the resolved data type. Calling code must instanceof-check or rely on a visitor.

How Vorro parses and produces Varies

Vorro treats Varies as a first-class discriminator dispatch. On inbound:

  • Each segment is parsed in two coordinated passes: discriminator fields first (OBX-2, CSU-3/4, etc.), then Varies fields with the resolved type in hand.
  • The resolved type is recorded alongside the value in the internal model, so downstream FHIR mappers and analytics never re-derive it.
  • Unknown HL70125 codes degrade to ST with a soft warning rather than failing the message; the original discriminator is preserved for audit.
  • Repetitions inside a Varies field inherit the single resolved type and are flattened into a typed list.

On outbound, Vorro emits OBX-2 (or the relevant sibling) before the Varies field in the same write pass, and refuses to serialize a Varies field whose discriminator is missing. We never default the discriminator silently.

Sources

← Back to HL7 v2 Guide

Ready to Integrate This Into Your Workflow?

Talk to a Vorro expert about implementing HL7 v2 in your specific environment.

Browse HL7 v2 Guides
HL7 Varies Data Type: Variant Field Resolved at Parse Time | Vorro Academy | Vorro