Repository: xlate/staedi (java)

Issue:
generated XML uses transaction element code instead of implementation element code?
### Discussed in https://github.com/xlate/staedi/discussions/277

<div type='discussions-op-text'>

<sup>Originally posted by **d-floyd** June 16, 2022</sup>
similar to this.. #274 
 I've defined a recurring element like this in the implementation xml:
```
                    <segment name="SubscriberIdentifier" typeId="SubscriberIdentifier" code="SubscriberIdentifier" type="REF" maxOccurs="1" discriminator="1">
                        <sequence>
                            <element position="1" title="Identification Qualifier">
                                <enumeration>
                                    <value>0F</value>
                                </enumeration>
                            </element>
                            <element position="2"/>
                        </sequence>
                    </segment>
                    <segment name="MemberPolicy" typeId="MemberPolicy" code="MemberPolicy" type="REF" maxOccurs="1" discriminator="1">
                        <sequence>
                            <element position="1" title="MemberPolicy Qualifier">
                                <enumeration>
                                    <value>1L</value>
                                </enumeration>
                            </element>
                            <element position="2"/>
                        </sequence>
                    </segment>
                    <segment  name="MemberSupplemental" typeId="MemberSupplemental"  code="MemberSupplemental" type="REF" maxOccurs="13" discriminator="1">
                        <sequence>
                            <element position="1" title="MemberPolicy Qualifier">
                                <enumeration>
                                    <value>17</value>
                                    <value>6O</value>
                                </enumeration>
                            </element>
                            <element position="2"/>
                        </sequence>
                    </segment>
```
in the transaction.xml I simply have this..
```
 <segment type="REF" maxOccurs="15" />
```
edi elements input:
```

REF*0F*5272937390~
REF*1L*5272937390~
REF*17*5272937390~
REF*6O*5700456720~
```


I was expecting this:
```
     <s:SubscriberIdentifier>
          <e:REF01>0F</e:REF01>
          <e:REF02>5272937390</e:REF02>
        </s:SubscriberIdentifier>
        <s:MemberPolicy>
          <e:REF01>1L</e:REF01>
          <e:REF02>5272937390</e:REF02>
        </s:MemberPolicy>
        <s:MemberSupplemental>
          <e:REF01>17</e:REF01>
          <e:REF02>5272937390</e:REF02>
        </s:MemberSupplemental>
        <s:MemberSupplemental>
          <e:REF01>6O</e:REF01>
          <e:REF02>5700456720</e:REF02>
        </s:MemberSupplemental>
```

but what I got was this:
```

        <s:REF>
          <e:REF01>0F</e:REF01>
          <e:REF02>5272937390</e:REF02>
        </s:REF>
        <s:REF>
          <e:REF01>1L</e:REF01>
          <e:REF02>5272937390</e:REF02>
        </s:REF>
        <s:REF>
          <e:REF01>17</e:REF01>
          <e:REF02>5272937390</e:REF02>
        </s:REF>
        <s:REF>
          <e:REF01>6O</e:REF01>
          <e:REF02>5700456720</e:REF02>
        </s:REF>
```


Does this not work the same with elements as it does with loops? Or is there something else I need set up?</div>

This is expected, but a configuration option to enable mapping the segment implementation's `code` to the XML element name would be a good enhancement.

cc: @d-floyd

Pull request:
Add option to read as XML using implementation segment codes
Fixes #278

Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java
index 12c240b..830d06c 100644
--- a/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java
+++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIInputFactory.java
@@ -45,6 +45,7 @@ public class StaEDIInputFactory extends EDIInputFactory {
 
         supportedProperties.add(XML_DECLARE_TRANSACTION_XMLNS);
         supportedProperties.add(XML_WRAP_TRANSACTION_CONTENTS);
+        supportedProperties.add(XML_USE_SEGMENT_IMPLEMENTATION_CODES);
 
         supportedProperties.add(JSON_NULL_EMPTY_ELEMENTS);
         supportedProperties.add(JSON_OBJECT_ELEMENTS);
diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java
index 6a19466..4ca92a4 100644
--- a/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java
+++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReader.java
@@ -54,11 +54,13 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
     private final Map<String, Object> properties;
     private final boolean transactionDeclaresXmlns;
     private final boolean wrapTransactionContents;
+    private final boolean useSegmentImplementationCodes;
     private final Location location = new ProxyLocation();
 
     private final Queue<Integer> eventQueue = new ArrayDeque<>(3);
     private final Queue<QName> elementQueue = new ArrayDeque<>(3);
     private final Deque<QName> elementStack = new ArrayDeque<>(5);
+    private final Deque<QName> standardNameStack = new ArrayDeque<>(5);
 
     private boolean withinTransaction = false;
     private boolean transactionWrapperEnqueued = false;
@@ -85,6 +87,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
         this.properties = new HashMap<>(properties);
         transactionDeclaresXmlns = Boolean.valueOf(String.valueOf(properties.get(EDIInputFactory.XML_DECLARE_TRANSACTION_XMLNS)));
         wrapTransactionContents = Boolean.valueOf(String.valueOf(properties.get(EDIInputFactory.XML_WRAP_TRANSACTION_CONTENTS)));
+        useSegmentImplementationCodes = Boolean.valueOf(String.valueOf(properties.get(EDIInputFactory.XML_USE_SEGMENT_IMPLEMENTATION_CODES)));
 
         if (ediReader.getEventType() == EDIStreamEvent.START_INTERCHANGE) {
             enqueueEvent(EDIStreamEvent.START_INTERCHANGE);
@@ -138,15 +141,29 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
     }
 
     private void enqueueEvent(int xmlEvent, QName element, boolean remember) {
+        enqueueEvent(xmlEvent, element, element, remember);
+    }
+
+    private void enqueueEvent(int xmlEvent, QName element, QName standardName, boolean remember) {
         LOGGER.finer(() -> "Enqueue XML event: " + xmlEvent + ", element: " + element);
         eventQueue.add(xmlEvent);
         elementQueue.add(element);
 
         if (remember) {
             elementStack.addFirst(element);
+            standardNameStack.addFirst(standardName);
         }
     }
 
+    QName parentName() {
+        return standardNameStack.getFirst();
+    }
+
+    QName popElement() {
+        standardNameStack.removeFirst();
+        return elementStack.removeFirst();
+    }
+
     private void advanceEvent() {
         currentEvent = eventQueue.remove();
         currentElement = elementQueue.remove();
@@ -161,7 +178,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
 
         switch (ediEvent) {
         case ELEMENT_DATA:
-            name = buildName(elementStack.getFirst(), EDINamespaces.ELEMENTS);
+            name = buildName(parentName(), EDINamespaces.ELEMENTS);
             enqueueEvent(START_ELEMENT, name, false);
             enqueueEvent(CHARACTERS, DUMMY_QNAME, false);
             enqueueEvent(END_ELEMENT, name, false);
@@ -172,7 +189,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
              * This section will read the binary data and Base64 the stream
              * into an XML CDATA section.
              * */
-            name = buildName(elementStack.getFirst(), EDINamespaces.ELEMENTS);
+            name = buildName(parentName(), EDINamespaces.ELEMENTS);
             enqueueEvent(START_ELEMENT, name, false);
             enqueueEvent(CDATA, DUMMY_QNAME, false);
 
@@ -201,31 +218,32 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
         case START_SEGMENT:
             readerText = ediReader.getText();
             performTransactionWrapping(readerText);
-            name = buildName(elementStack.getFirst(), EDINamespaces.SEGMENTS, readerText);
-            enqueueEvent(START_ELEMENT, name, true);
+            QName standardName = buildName(parentName(), EDINamespaces.SEGMENTS, readerText);
+            name = useSegmentImplementationCodes ? buildName(parentName(), EDINamespaces.SEGMENTS, ediReader.getReferenceCode()) : standardName;
+            enqueueEvent(START_ELEMENT, name, standardName, true);
             break;
 
         case START_TRANSACTION:
             withinTransaction = true;
-            name = buildName(elementStack.getFirst(), EDINamespaces.LOOPS, ediReader.getReferenceCode());
+            name = buildName(parentName(), EDINamespaces.LOOPS, ediReader.getReferenceCode());
             enqueueEvent(START_ELEMENT, name, true);
             determineTransactionSegments();
             break;
 
         case START_GROUP:
         case START_LOOP:
-            name = buildName(elementStack.getFirst(), EDINamespaces.LOOPS, ediReader.getReferenceCode());
+            name = buildName(parentName(), EDINamespaces.LOOPS, ediReader.getReferenceCode());
             enqueueEvent(START_ELEMENT, name, true);
             break;
 
         case START_COMPOSITE:
             compositeCode = ediReader.getReferenceCode();
-            name = buildName(elementStack.getFirst(), EDINamespaces.COMPOSITES);
+            name = buildName(parentName(), EDINamespaces.COMPOSITES);
             enqueueEvent(START_ELEMENT, name, true);
             break;
 
         case END_INTERCHANGE:
-            enqueueEvent(END_ELEMENT, elementStack.removeFirst(), false);
+            enqueueEvent(END_ELEMENT, popElement(), false);
             namespaceContext = null;
             enqueueEvent(END_DOCUMENT, DUMMY_QNAME, false);
             break;
@@ -233,7 +251,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
         case END_TRANSACTION:
             withinTransaction = false;
             compositeCode = null;
-            enqueueEvent(END_ELEMENT, elementStack.removeFirst(), false);
+            enqueueEvent(END_ELEMENT, popElement(), false);
             break;
 
         case END_GROUP:
@@ -241,7 +259,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
         case END_SEGMENT:
         case END_COMPOSITE:
             compositeCode = null;
-            enqueueEvent(END_ELEMENT, elementStack.removeFirst(), false);
+            enqueueEvent(END_ELEMENT, popElement(), false);
             break;
 
         case SEGMENT_ERROR:
@@ -278,7 +296,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
         if (withinTransaction && wrapTransactionContents) {
             if (transactionWrapperEnqueued) {
                 if (readerText.equals(this.transactionEndSegment)) {
-                    enqueueEvent(END_ELEMENT, elementStack.removeFirst(), false);
+                    enqueueEvent(END_ELEMENT, popElement(), false);
                     transactionWrapperEnqueued = false;
                 }
             } else {
@@ -402,6 +420,7 @@ final class StaEDIXMLStreamReader implements XMLStreamReader {
             eventQueue.clear();
             elementQueue.clear();
             elementStack.clear();
+            standardNameStack.clear();
             ediReader.close();
         } catch (IOException e) {
             throw new XMLStreamException(e);
diff --git a/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java b/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
index 73c10b8..20a0d58 100644
--- a/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
+++ b/src/main/java/io/xlate/edi/internal/stream/tokenization/ProxyEventHandler.java
@@ -356,7 +356,6 @@ public class ProxyEventHandler implements EventHandler {
     public boolean elementData(char[] text, int start, int length) {
         boolean derivedComposite;
         EDIReference typeReference;
-        boolean eventsReady = true;
         final boolean compositeFromStream = location.getComponentPosition() > -1;
 
         elementHolder.set(text, start, length);
@@ -368,31 +367,36 @@ public class ProxyEventHandler implements EventHandler {
             setLevelIdentifiers();
         }
 
+        /*
+         * The first component of a composite was the only element received
+         * for the composite. It was found to be a composite via the schema
+         * and the composite begin/end events must be generated.
+         **/
+        final boolean componentReceivedAsSimple;
+
         if (validator != null) {
+            derivedComposite = !compositeFromStream && validator.isComposite(dialect, location);
+            componentReceivedAsSimple = derivedComposite && text != null;
+
+            if (componentReceivedAsSimple) {
+                this.compositeBegin(elementHolder.length() == 0);
+                location.incrementComponentPosition();
+            }
+
             valid = validator.validateElement(dialect, location, elementHolder, null);
-            derivedComposite = !compositeFromStream && validator.isComposite();
             typeReference = validator.getElementReference();
             enqueueElementOccurrenceErrors(validator, valid);
         } else {
             valid = true;
             derivedComposite = false;
+            componentReceivedAsSimple = false;
             typeReference = null;
         }
 
-        /*
-         * The first component of a composite was the only element received
-         * for the composite. It was found to be a composite via the schema
-         * and the composite begin/end events must be generated.
-         **/
-        final boolean componentReceivedAsSimple = derivedComposite && text != null;
-
-        if (componentReceivedAsSimple) {
-            this.compositeBegin(elementHolder.length() == 0);
-            location.incrementComponentPosition();
-        }
-
         enqueueElementErrors(validator, valid);
 
+        boolean eventsReady = true;
+
         if (text != null && (!derivedComposite || length > 0) /* Not an inferred element */) {
             enqueueEvent(EDIStreamEvent.ELEMENT_DATA,
                          EDIStreamValidationError.NONE,
@@ -400,9 +404,7 @@ public class ProxyEventHandler implements EventHandler {
                          typeReference,
                          location);
 
-            if (validator != null && validator.isPendingDiscrimination()) {
-                eventsReady = validator.selectImplementation(eventQueue, this);
-            }
+            eventsReady = selectImplementationIfPending(validator, eventsReady);
         }
 
         if (componentReceivedAsSimple) {
@@ -413,6 +415,14 @@ public class ProxyEventHandler implements EventHandler {
         return !levelCheckPending && eventsReady;
     }
 
+    boolean selectImplementationIfPending(Validator validator, boolean eventsReadyDefault) {
+        if (validator != null && validator.isPendingDiscrimination()) {
+            return validator.selectImplementation(eventQueue, this);
+        }
+
+        return eventsReadyDefault;
+    }
+
     void clearLevelCheck() {
         levelCheckPending = false;
         currentSegmentBegin = null;
diff --git a/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java b/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java
index 8609ecc..3259f27 100644
--- a/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java
+++ b/src/main/java/io/xlate/edi/internal/stream/validation/Validator.java
@@ -1022,10 +1022,30 @@ public class Validator {
         return elementErrors.isEmpty();
     }
 
-    public boolean isComposite() {
+    boolean isComposite() {
+        return isComposite(this.composite);
+    }
+
+    static boolean isComposite(UsageNode composite) {
         return composite != null && !StaEDISchema.ANY_COMPOSITE_ID.equals(composite.getId());
     }
 
+    public boolean isComposite(Dialect dialect, StaEDIStreamLocation position) {
+        if (!segmentExpected) {
+            return false;
+        }
+
+        final String version = dialect.getTransactionVersionString();
+        final int elementPosition = position.getElementPosition() - 1;
+
+        if (elementPosition < segment.getChildren(version).size()) {
+            UsageNode candidate = segment.getChild(version, elementPosition);
+            return candidate.isNodeType(EDIType.Type.COMPOSITE) && isComposite(candidate);
+        }
+
+        return false;
+    }
+
     public boolean validateElement(Dialect dialect, StaEDIStreamLocation position, CharSequence value, StringBuilder formattedValue) {
         if (!segmentExpected) {
             return true;
@@ -1104,7 +1124,7 @@ public class Validator {
             String version = dialect.getTransactionVersionString();
 
             if (componentIndex < element.getChildren(version).size()) {
-                if (valueReceived || componentIndex != 0 /* Derived component*/) {
+                if (valueReceived || componentIndex != 0 /* Derived component */) {
                     this.element = this.element.getChild(version, componentIndex);
 
                     if (isImplElementSelected()) {
diff --git a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java
index 1f85f80..f074c42 100644
--- a/src/main/java/io/xlate/edi/stream/EDIInputFactory.java
+++ b/src/main/java/io/xlate/edi/stream/EDIInputFactory.java
@@ -53,6 +53,16 @@ public abstract class EDIInputFactory extends PropertySupport {
      */
     public static final String XML_WRAP_TRANSACTION_CONTENTS = "io.xlate.edi.stream.XML_WRAP_TRANSACTION_CONTENTS";
 
+    /**
+     * When set to true, the XML elements representing segments in an EDI implementation schema
+     * will be named according to the schema-defined {@code code} attribute for the segment.
+     *
+     * Default value: false
+     *
+     * @since 1.21
+     */
+    public static final String XML_USE_SEGMENT_IMPLEMENTATION_CODES = "io.xlate.edi.stream.XML_USE_SEGMENT_IMPLEMENTATION_CODES";
+
     /**
      * When set to true, non-graphical, control characters will be ignored in
      * the EDI input stream. This includes characters ranging from 0x00 through
diff --git a/src/main/resources/schema/EDISchema-v3.xsd b/src/main/resources/schema/EDISchema-v3.xsd
index 013cce6..e0ffb4a 100644
--- a/src/main/resources/schema/EDISchema-v3.xsd
+++ b/src/main/resources/schema/EDISchema-v3.xsd
@@ -241,6 +241,14 @@
           </element>
         </sequence>
         <attribute name="type" type="IDREF" use="required" />
+        <attribute name="code" type="NMTOKEN" use="optional">
+          <annotation>
+            <documentation>
+              Code used to identify a segment within a loop. May be useful when occurrences of a
+              standard segment contain distinct information.
+            </documentation>
+          </annotation>
+        </attribute>
         <attributeGroup ref="tns:implementationAttributeGroup" />
         <attribute name="discriminator" type="tns:discriminatorType" use="optional">
           <annotation>


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReaderTest.java b/src/test/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReaderTest.java
index 1ebd0b9..5f1bd0e 100644
--- a/src/test/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReaderTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIXMLStreamReaderTest.java
@@ -945,4 +945,40 @@ class StaEDIXMLStreamReaderTest {
         assertTrue(!d.hasDifferences(), () -> "XML unexpectedly different:\n" + d.toString(new DefaultComparisonFormatter()));
     }
 
+    @Test
+    void testImplementationSegmentCodeUsage() throws Exception {
+        EDIInputFactory ediFactory = EDIInputFactory.newFactory();
+        ediFactory.setProperty(EDIInputFactory.XML_USE_SEGMENT_IMPLEMENTATION_CODES, Boolean.TRUE);
+        ediFactory.setErrorReporter((errorType, reader) -> {
+            // NO-OP
+        });
+        SchemaFactory schemaFactory = SchemaFactory.newFactory();
+
+        InputStream stream = getClass().getResourceAsStream("/x12/simple999.edi");
+        ediReader = ediFactory.createEDIStreamReader(stream);
+        Schema schema = schemaFactory.createSchema(getClass().getResource("/x12/IG-999-standard-included.xml"));
+
+        ediReader = ediFactory.createFilteredReader(ediReader, (reader) -> {
+            if (reader.getEventType() == EDIStreamEvent.START_TRANSACTION) {
+                reader.setTransactionSchema(schema);
+            }
+            return true;
+        });
+
+        XMLStreamReader xmlReader = ediFactory.createXMLStreamReader(ediReader);
+
+        xmlReader.next(); // Per StAXSource JavaDoc, put in START_DOCUMENT state
+        TransformerFactory factory = TransformerFactory.newInstance();
+        Transformer transformer = factory.newTransformer();
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+        StringWriter result = new StringWriter();
+        transformer.transform(new StAXSource(xmlReader), new StreamResult(result));
+        String resultString = result.toString();
+        System.out.println(resultString);
+        Diff d = DiffBuilder.compare(Input.fromFile("src/test/resources/x12/impl-ack-with-segment-impl-codes.xml"))
+                .withTest(resultString).build();
+        assertTrue(!d.hasDifferences(), () -> "XML unexpectedly different:\n" + d.toString(new DefaultComparisonFormatter()));
+    }
+
 }
diff --git a/src/test/resources/x12/IG-999-standard-included-relative.xml b/src/test/resources/x12/IG-999-standard-included-relative.xml
index 538d277..6fdf4b3 100644
--- a/src/test/resources/x12/IG-999-standard-included-relative.xml
+++ b/src/test/resources/x12/IG-999-standard-included-relative.xml
@@ -30,7 +30,7 @@
                   <element position="4" minOccurs="1" />
                 </sequence>
               </segment>
-              <segment type="CTX" discriminator="01.01" maxOccurs="9">
+              <segment type="CTX" discriminator="01.01" maxOccurs="9" code="SegmentContext">
                 <sequence>
                   <composite position="1">
                     <sequence>
@@ -52,7 +52,7 @@
                   </composite>
                 </sequence>
               </segment>
-              <segment type="CTX" maxOccurs="1">
+              <segment type="CTX" maxOccurs="1" code="BusinessUnitIdentifier">
                 <sequence>
                   <composite position="1">
                     <sequence>
@@ -68,7 +68,7 @@
                   <segment type="IK4">
                       <!-- Unchanged from standard -->
                   </segment>
-                  <segment type="CTX" maxOccurs="10">
+                  <segment type="CTX" maxOccurs="10" code="ElementContext">
                     <sequence>
                       <composite position="1">
                         <sequence>
diff --git a/src/test/resources/x12/IG-999-standard-included.xml b/src/test/resources/x12/IG-999-standard-included.xml
index f8e4f63..b9c7ec3 100644
--- a/src/test/resources/x12/IG-999-standard-included.xml
+++ b/src/test/resources/x12/IG-999-standard-included.xml
@@ -30,7 +30,7 @@
                   <element position="4" minOccurs="1" />
                 </sequence>
               </segment>
-              <segment type="CTX" discriminator="01.01" maxOccurs="9">
+              <segment type="CTX" discriminator="01.01" maxOccurs="9" code="SegmentContext">
                 <sequence>
                   <composite position="1">
                     <sequence>
@@ -52,7 +52,7 @@
                   </composite>
                 </sequence>
               </segment>
-              <segment type="CTX" maxOccurs="1">
+              <segment type="CTX" maxOccurs="1" code="BusinessUnitIdentifier">
                 <sequence>
                   <composite position="1">
                     <sequence>
@@ -68,7 +68,7 @@
                   <segment type="IK4">
                       <!-- Unchanged from standard -->
                   </segment>
-                  <segment type="CTX" maxOccurs="10">
+                  <segment type="CTX" maxOccurs="10" code="ElementContext">
                     <sequence>
                       <composite position="1">
                         <sequence>
diff --git a/src/test/resources/x12/IG-999.xml b/src/test/resources/x12/IG-999.xml
index 9fc981e..613b252 100644
--- a/src/test/resources/x12/IG-999.xml
+++ b/src/test/resources/x12/IG-999.xml
@@ -53,7 +53,7 @@
                   <element position="4" minOccurs="1" />
                 </sequence>
               </segment>
-              <segment type="CTX" discriminator="01.01" maxOccurs="9">
+              <segment type="CTX" discriminator="01.01" maxOccurs="9" code="SegmentContext">
                 <sequence>
                   <composite position="1">
                     <sequence>
@@ -75,7 +75,7 @@
                   </composite>
                 </sequence>
               </segment>
-              <segment type="CTX" maxOccurs="1">
+              <segment type="CTX" maxOccurs="1" code="BusinessUnitIdentifier">
                 <sequence>
                   <composite position="1">
                     <sequence>
@@ -91,7 +91,7 @@
                   <segment type="IK4">
                       <!-- Unchanged from standard -->
                   </segment>
-                  <segment type="CTX" maxOccurs="10">
+                  <segment type="CTX" maxOccurs="10" code="ElementContext">
                     <sequence>
                       <composite position="1">
                         <sequence>
diff --git a/src/test/resources/x12/impl-ack-with-segment-impl-codes.xml b/src/test/resources/x12/impl-ack-with-segment-impl-codes.xml
new file mode 100644
index 0000000..73a2684
--- /dev/null
+++ b/src/test/resources/x12/impl-ack-with-segment-impl-codes.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?><l:INTERCHANGE xmlns:l="urn:xlate.io:staedi:names:loops" xmlns:s="urn:xlate.io:staedi:names:segments" xmlns:c="urn:xlate.io:staedi:names:composites" xmlns:e="urn:xlate.io:staedi:names:elements">
+  <s:ISA>
+    <e:ISA01>00</e:ISA01>
+    <e:ISA02>          </e:ISA02>
+    <e:ISA03>00</e:ISA03>
+    <e:ISA04>          </e:ISA04>
+    <e:ISA05>ZZ</e:ISA05>
+    <e:ISA06>123456789      </e:ISA06>
+    <e:ISA07>ZZ</e:ISA07>
+    <e:ISA08>987654321      </e:ISA08>
+    <e:ISA09>041117</e:ISA09>
+    <e:ISA10>1024</e:ISA10>
+    <e:ISA11>^</e:ISA11>
+    <e:ISA12>00501</e:ISA12>
+    <e:ISA13>000000286</e:ISA13>
+    <e:ISA14>0</e:ISA14>
+    <e:ISA15>P</e:ISA15>
+    <e:ISA16>:</e:ISA16>
+  </s:ISA>
+  <l:GROUP>
+    <s:GS>
+      <e:GS01>FA</e:GS01>
+      <e:GS02>RCVR</e:GS02>
+      <e:GS03>SNDR</e:GS03>
+      <e:GS04>20041117</e:GS04>
+      <e:GS05>1024</e:GS05>
+      <e:GS06>287</e:GS06>
+      <e:GS07>X</e:GS07>
+      <e:GS08>005010X231</e:GS08>
+    </s:GS>
+    <l:TRANSACTION>
+      <s:ST>
+        <e:ST01>999</e:ST01>
+        <e:ST02>2870001</e:ST02>
+        <e:ST03>005010X231</e:ST03>
+      </s:ST>
+      <s:AK1>
+        <e:AK101>HC</e:AK101>
+        <e:AK102>17456</e:AK102>
+        <e:AK103>005010X222</e:AK103>
+      </s:AK1>
+      <l:AK2>
+        <s:AK2>
+          <e:AK201>837</e:AK201>
+          <e:AK202>0001</e:AK202>
+        </s:AK2>
+        <s:IK5>
+          <e:IK501>A</e:IK501>
+        </s:IK5>
+      </l:AK2>
+      <l:AK2>
+        <s:AK2>
+          <e:AK201>837</e:AK201>
+          <e:AK202>0002</e:AK202>
+        </s:AK2>
+        <l:IK3>
+          <s:IK3>
+            <e:IK301>CLM</e:IK301>
+            <e:IK302>22</e:IK302>
+            <e:IK303/>
+            <e:IK304>8</e:IK304>
+          </s:IK3>
+          <s:BusinessUnitIdentifier>
+            <c:CTX01>
+              <e:C998-01>CLM01</e:C998-01>
+              <e:C998-02>123456789</e:C998-02>
+            </c:CTX01>
+          </s:BusinessUnitIdentifier>
+          <l:IK4>
+            <s:IK4>
+              <c:IK401>
+                <e:C030-01>2</e:C030-01>
+              </c:IK401>
+              <e:IK402>782</e:IK402>
+              <e:IK403>1</e:IK403>
+            </s:IK4>
+          </l:IK4>
+        </l:IK3>
+        <s:IK5>
+          <e:IK501>R</e:IK501>
+          <e:IK502>5</e:IK502>
+        </s:IK5>
+      </l:AK2>
+      <l:AK2>
+        <s:AK2>
+          <e:AK201>837</e:AK201>
+          <e:AK202>0003</e:AK202>
+        </s:AK2>
+        <l:IK3>
+          <s:IK3>
+            <e:IK301>REF</e:IK301>
+            <e:IK302>57</e:IK302>
+            <e:IK303/>
+            <e:IK304>3</e:IK304>
+          </s:IK3>
+          <s:SegmentContext>
+            <c:CTX01>
+              <e:C998-01>SITUATIONAL TRIGGER</e:C998-01>
+            </c:CTX01>
+            <c:CTX01>
+              <e:C998-01>SITUATIONAL TRIGGER</e:C998-01>
+              <e:C998-02>2</e:C998-02>
+            </c:CTX01>
+            <c:CTX01>
+              <e:C998-01>SITUATIONAL TRIGGER</e:C998-01>
+              <e:C998-02>3</e:C998-02>
+            </c:CTX01>
+            <e:CTX02>CLM</e:CTX02>
+            <e:CTX03>43</e:CTX03>
+            <e:CTX04/>
+            <c:CTX05>
+              <e:C030-01>5</e:C030-01>
+              <e:C030-02>3</e:C030-02>
+            </c:CTX05>
+            <c:CTX06>
+              <e:C999-01>1325</e:C999-01>
+            </c:CTX06>
+          </s:SegmentContext>
+          <s:BusinessUnitIdentifier>
+            <c:CTX01>
+              <e:C998-01>CLM01</e:C998-01>
+              <e:C998-02>987654321</e:C998-02>
+            </c:CTX01>
+          </s:BusinessUnitIdentifier>
+        </l:IK3>
+        <s:IK5>
+          <e:IK501>R</e:IK501>
+          <e:IK502>5</e:IK502>
+        </s:IK5>
+      </l:AK2>
+      <s:AK9>
+        <e:AK901>P</e:AK901>
+        <e:AK902>3</e:AK902>
+        <e:AK903>3</e:AK903>
+        <e:AK904>1</e:AK904>
+      </s:AK9>
+      <s:SE>
+        <e:SE01>16</e:SE01>
+        <e:SE02>2870001</e:SE02>
+      </s:SE>
+    </l:TRANSACTION>
+    <s:GE>
+      <e:GE01>1</e:GE01>
+      <e:GE02>287</e:GE02>
+    </s:GE>
+  </l:GROUP>
+  <s:IEA>
+    <e:IEA01>1</e:IEA01>
+    <e:IEA02>000000286</e:IEA02>
+  </s:IEA>
+</l:INTERCHANGE>
\ No newline at end of file