Skip to content

Reading EDI Data (JSON Conversion)

Michael Edgar edited this page Jul 28, 2022 · 3 revisions

This article assumes the reader has basic knowledge of EDI structures and terminology

Reading EDI into Jakarta JSON Structures

The Jakarta JSON Processing (aka Java API for JSON Processing or JSR 374) is an API to allow generic interaction with a JSON structure. The example below demonstrates how to read a simple X12 997 file, validate the stream, and load into a JSON structure (presumably for further downstream processing).

Input File (X12 997, version 005010):

ISA*00*          *00*          *ZZ*ReceiverID     *ZZ*Sender         *050812*1953*^*00501*508121953*0*P*:~
GS*FA*ReceiverDept*SenderDept*20050812*195335*000005*X*005010X230~
ST*997*0001~
AK1*HC*000001~
AK2*837*0021~
AK3*NM1*8**8~
AK4*8*66*7*MI~
AK5*R*5~
AK9*R*1*1*0~
SE*8*0001~
GE*1*000005~
IEA*1*508121953~

EDI schema used for validation:

<schema xmlns="http://xlate.io/EDISchema/v4">
  <transaction>
    <sequence>
      <segment type="AK1" minOccurs="1" />
      <loop code="2000" maxOccurs="999999">
        <sequence>
          <segment type="AK2" />
          <loop code="2100" maxOccurs="999999">
            <sequence>
              <segment type="AK3" />
              <segment type="AK4" maxOccurs="99" />
            </sequence>
          </loop>
          <segment type="AK5" minOccurs="1" />
        </sequence>
      </loop>
      <segment type="AK9" minOccurs="1" />
    </sequence>
  </transaction>

  <elementType name="E0002" code="2" base="numeric" maxLength="6" />
  <elementType name="E0028" code="28" base="numeric" maxLength="9" />
  <elementType name="E0097" code="97" base="numeric" maxLength="6" />
  <elementType name="E0123" code="123" base="numeric" maxLength="6" />
  <elementType name="E0143" code="143" base="string" minLength="3" maxLength="3">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0329" code="329" base="string" minLength="4" maxLength="9" />
  <elementType name="E0447" code="447" base="string" maxLength="4" />
  <elementType name="E0479" code="479" base="string" minLength="2" maxLength="2">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0480" code="480" base="string" maxLength="12" />
  <elementType name="E0715" code="715" base="string">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0716" code="716" base="string" maxLength="3">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0717" code="717" base="string">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0718" code="718" base="string" maxLength="3">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0719" code="719" base="numeric" maxLength="10" />
  <elementType name="E0720" code="720" base="string" maxLength="3">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0721" code="721" base="string" minLength="2" maxLength="3" />
  <elementType name="E0722" code="722" base="numeric" maxLength="2" />
  <elementType name="E0723" code="723" base="string" maxLength="3">
    <!-- Enumerated values omitted for example -->
  </elementType>
  <elementType name="E0724" code="724" base="string" maxLength="99" />
  <elementType name="E0725" code="725" base="numeric" maxLength="4" />
  <elementType name="E1528" code="1528" base="numeric" maxLength="2" />
  <elementType name="E1686" code="1686" base="numeric" maxLength="4" />
  <elementType name="E1705" code="1705" base="string" maxLength="35" />
  <compositeType name="C030">
    <sequence>
      <element type="E0722" minOccurs="1" />
      <element type="E1528" />
      <element type="E1686" />
    </sequence>
  </compositeType>
  <segmentType name="AK1">
    <sequence>
      <element type="E0479" minOccurs="1" />
      <element type="E0028" minOccurs="1" />
      <element type="E0480" />
    </sequence>
  </segmentType>
  <segmentType name="AK2">
    <sequence>
      <element type="E0143" minOccurs="1" />
      <element type="E0329" minOccurs="1" />
      <element type="E1705" />
    </sequence>
  </segmentType>
  <segmentType name="AK3">
    <sequence>
      <element type="E0721" minOccurs="1" />
      <element type="E0719" minOccurs="1" />
      <element type="E0447" />
      <element type="E0720" />
    </sequence>
  </segmentType>
  <segmentType name="AK4">
    <sequence>
      <composite type="C030" minOccurs="1" />
      <element type="E0725" />
      <element type="E0723" minOccurs="1" />
      <element type="E0724" />
    </sequence>
  </segmentType>
  <segmentType name="AK5">
    <sequence>
      <element type="E0717" minOccurs="1" />
      <element type="E0718" />
      <element type="E0718" />
      <element type="E0718" />
      <element type="E0718" />
      <element type="E0718" />
    </sequence>
  </segmentType>
  <segmentType name="AK9">
    <sequence>
      <element type="E0715" minOccurs="1" />
      <element type="E0097" minOccurs="1" />
      <element type="E0123" minOccurs="1" />
      <element type="E0002" minOccurs="1" />
      <element type="E0716" />
      <element type="E0716" />
      <element type="E0716" />
      <element type="E0716" />
      <element type="E0716" />
    </sequence>
  </segmentType>
</schema>

The following Java code naively constructs a JsonObject using the EDI input stream, include with loop structure provided by the EDI schema provided to the reader via setTransactionSchema. While building the JSON, this example does not consider that loops and segments may repeat, and as such should be represent by arrays in the JSON. A production application would need to take that structure into consideration.

package com.example;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;

import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.stream.JsonGenerator;

import io.xlate.edi.schema.Schema;
import io.xlate.edi.schema.SchemaFactory;
import io.xlate.edi.stream.EDIInputFactory;
import io.xlate.edi.stream.EDIStreamEvent;
import io.xlate.edi.stream.EDIStreamReader;
import io.xlate.edi.stream.EDIStreamValidationError;

public class ReadFuncAcknowledgement {

    void readFuncAcknowledgement() throws Exception {
        EDIInputFactory factory = EDIInputFactory.newFactory();
        JsonObject result = null;

        // Any InputStream can be used to create an `EDIStreamReader`
        try (InputStream stream = new FileInputStream("simple997.edi");
             EDIStreamReader reader = factory.createEDIStreamReader(stream)) {

            EDIStreamEvent event;
            boolean transactionBeginSegment = false;
            Deque<JsonObjectBuilder> buildStack = new ArrayDeque<>();
            JsonObjectBuilder builder = null;
            JsonArrayBuilder transactions = null;
            JsonArrayBuilder segmentBuilder = null;
            JsonArrayBuilder compositeBuilder = null;

            while (reader.hasNext()) {
                event = reader.next();

                switch (event) {
                case START_INTERCHANGE:
                    // Called at the beginning of the EDI stream once the X12 dialect is confirmed
                    builder = Json.createObjectBuilder();
                    buildStack.offer(builder);
                    break;
                case END_INTERCHANGE:
                    // Called following the IEA segment
                    result = builder.build();
                    break;

                case START_GROUP:
                    // Called prior to the start of the GS segment
                    builder = Json.createObjectBuilder();
                    buildStack.offer(builder);
                    transactions = Json.createArrayBuilder();
                    break;
                case END_GROUP:
                    // Called following the GE segment
                    JsonObjectBuilder groupBuilder = buildStack.removeLast();
                    groupBuilder.add("TRANSACTIONS", transactions);
                    builder = buildStack.peekLast();
                    builder.add(reader.getReferenceCode(), groupBuilder);
                    break;

                case START_TRANSACTION:
                    // Called prior to the start of the ST segment
                    builder = Json.createObjectBuilder();
                    buildStack.offer(builder);
                    // Set a boolean so that when the ST segment is complete, our code
                    // can set a transaction schema to use with the reader. This boolean
                    // allows the END_SEGMENT code to know the current context of the
                    // segment.
                    transactionBeginSegment = true;
                    break;
                case END_TRANSACTION:
                    // Called following the SE segment
                    JsonObjectBuilder transactionBuilder = buildStack.removeLast();
                    transactions.add(transactionBuilder);
                    builder = buildStack.peekLast();
                    break;

                case START_LOOP:
                    // Called before the start of the segment that begins a loop.
                    // The loop's `code` from the schema can be obtained by a call
                    // to `reader.getReferenceCode()`
                    builder = Json.createObjectBuilder();
                    buildStack.offer(builder);
                    break;
                case END_LOOP:
                    // Called following the end of the segment that ends a loop.
                    // The loop's `code` from the schema can be obtained by a call
                    // to `reader.getReferenceCode()`
                    JsonObjectBuilder loopBuilder = buildStack.removeLast();
                    builder = buildStack.peekLast();
                    builder.add(reader.getReferenceCode(), loopBuilder);
                    break;

                case START_SEGMENT:
                    segmentBuilder = Json.createArrayBuilder();
                    break;

                case END_SEGMENT:
                    if (transactionBeginSegment) {
                        /*
                         * At the end of the ST segment, load the schema for use to validate
                         * the transaction.
                         */
                        SchemaFactory schemaFactory = SchemaFactory.newFactory();
                        // Any InputStream or URL can be used to create a `Schema`
                        Schema schema = schemaFactory.createSchema(new FileInputStream("EDISchema997.xml"));
                        reader.setTransactionSchema(schema);
                    }
                    transactionBeginSegment = false;
                    builder.add(reader.getText(), segmentBuilder);
                    segmentBuilder = null;
                    break;

                case START_COMPOSITE:
                    compositeBuilder = Json.createArrayBuilder();
                    break;
                case END_COMPOSITE:
                    segmentBuilder.add(compositeBuilder);
                    compositeBuilder = null;
                    break;

                case ELEMENT_DATA:
                    if (compositeBuilder != null) {
                        compositeBuilder.add(reader.getText());
                    } else {
                        segmentBuilder.add(reader.getText());
                    }
                    break;

                case SEGMENT_ERROR:
                    // Handle a segment error
                    EDIStreamValidationError segmentErrorType = reader.getErrorType();
                    // ...
                    break;

                case ELEMENT_OCCURRENCE_ERROR:
                case ELEMENT_DATA_ERROR:
                    // Handle a segment error
                    EDIStreamValidationError elementErrorType = reader.getErrorType();
                    // ...
                    break;

                default:
                    break;
                }
            }
        }

        Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, "true"))
            .createGenerator(System.out)
            .write(result)
            .close();
    }
}

Resulting JSON output:

{
    "ISA": [
        "00",
        "          ",
        "00",
        "          ",
        "ZZ",
        "ReceiverID     ",
        "ZZ",
        "Sender         ",
        "050812",
        "1953",
        "^",
        "00501",
        "508121953",
        "0",
        "P",
        ":"
    ],
    "GROUP": {
        "GS": [
            "FA",
            "ReceiverDept",
            "SenderDept",
            "20050812",
            "195335",
            "000005",
            "X",
            "005010X230"
        ],
        "GE": [
            "1",
            "000005"
        ],
        "TRANSACTIONS": [ {
            "ST": [
                "997",
                "0001"
            ],
            "AK1": [
                "HC",
                "000001"
            ],
            "2000": {
                "AK2": [
                    "837",
                    "0021"
                ],
                "2100": {
                    "AK3": [
                        "NM1",
                        "8",
                        "",
                        "8"
                    ],
                    "AK4": [
                        [
                            "8"
                        ],
                        "66",
                        "7",
                        "MI"
                    ]
                },
                "AK5": [
                    "R",
                    "5"
                ]
            },
            "AK9": [
                "R",
                "1",
                "1",
                "0"
            ],
            "SE": [
                "8",
                "0001"
            ]
        } ]
    },
    "IEA": [
        "1",
        "508121953"
    ]
}