Guides

Modifying and building

A system that reads HL7 usually has to answer in it. Setting values, adding segments, rendering valid ER7 back out, and building an acknowledgement that names the message it answers.

Setting a value

set takes the same path grammar as get, so anything you can read you can write.

rust
let mut message = hl7_2::parse(text)?;

message.set("PID-5.2", "EVELYN")?;
message.set("PID-3[2].1", "999888777")?;
message.set("MSH-7", "20260814080100")?;

assert_eq!(message.get("PID-5.2")?.as_deref(), Some("EVELYN"));

Setting a path that does not exist yet creates the intermediate structure — components, repetitions, and fields are filled in as needed. Setting a path in a segment that does not exist is an error; add the segment first (below).

Escaping happens for you

A value that contains a delimiter character has to be escaped, or it will re-parse as extra structure. set does that.

rust
// The value contains a field separator. You do not escape it yourself.
message.set("OBX-5", "Reading was 90|100 mmHg")?;

// The rendered ER7 carries \F\ where the pipe was, so the message still
// parses as one field — and reading it back gives you the pipe again.
assert_eq!(message.get("OBX-5")?.as_deref(), Some("Reading was 90|100 mmHg"));

Deleting versus emptying

HL7 has two different “no value”, and in an update message they do opposite things. Be deliberate about which one you write.

rust
// "Delete the value you currently hold for this field."
message.set_null("PID-11")?;

// "Send nothing about this field." Not the same thing.
message.set("PID-11", "")?;

See the HL7 null for how each is encoded in each format.

Adding segments

rust
message.append_segment("NTE");
message.set("NTE[2]-1", "2")?;
message.set("NTE[2]-3", "Amended after review.")?;

The new segment goes at the end. Note the index in the paths afterwards: if the message already had one NTE, the one you just added is NTE[2].

Rendering back to ER7

rust
let er7 = message.to_er7();

// An unmodified message writes back byte for byte. That guarantee is er7's,
// and hl7-2 does not weaken it — so a read-modify-write touches only what
// you actually changed.

Byte-for-byte round-tripping of an unmodified message is a property of the er7 layer, and it matters more than it might seem: it means a message that passes through your system unchanged is provably unchanged, which is a question auditors ask.

Building a message from nothing

hl7_2::Builder constructs a message rather than editing one. Its encode method takes a value implementing ToHl7, so a struct with derive can be written out directly. build_valid runs validation and refuses to hand you something the dictionary says is malformed.

Acknowledgements

The reply an HL7 v2 sender waits for is an HL7 message: an ACK whose MSA-2 echoes the control ID of the message being answered.

rust
let ack = hl7_2::builder::acknowledge(&message, "AA", "ACK00001", "20260814080100")
    .build_valid()?;

assert_eq!(ack.get("MSA-1")?.as_deref(), Some("AA"));
assert_eq!(ack.get("MSA-2")?.as_deref(), Some("MSG00042"));

That echo is the whole mechanism. A sender that does not compare it will eventually take one answer for another's — which is why every call here takes the acknowledgement's own control ID and timestamp as arguments rather than inventing them. A message that invents them is untestable and untraceable.

To reject something, say why:

rust
let mut nack = hl7_2::builder::acknowledge(&message, "AE", "N1", "20260814080100")
    .build_valid()?;
nack.set("MSA-3", "OBR-4 is required")?;

The codes are HL7's: AA accept, AE error, AR reject. MSA-3 is the human-readable reason. To put one on a socket, see MLLP acknowledgement.

The same, from the shell

sh
# Change something and write it back out
hl7-v2 --set 'PID-8=F' --er7 in.hl7 > out.hl7

# Several edits in one pass; --set is repeatable
hl7-v2 --set 'PID-8=F' --set 'PID-5.2=EVELYN' --er7 in.hl7

# Write the explicit HL7 null
hl7-v2 --null 'PID-11' --er7 in.hl7

Useful for a one-off correction to a stuck message without writing a program. Full flags: Command line.