EDI Translator & Parser

EdiFabric translates EDI documents by implementing a DFS (depth-first-search) algorithm. EDI documents are transposed into C# instances of the corresponding EDI template class.

EdiReader provides fast, non-cached, forward-only access to EDI data. EdiReader methods let you move through EDI documents and read the contents of transactions and control segments. The properties of the class reflect the value of the current node, which is where the reader is positioned.

The CurrentInterchangeHeader property indicates the current interchange header segment. The CurrentGroupHeader property indicates the current group header segment. The Item property indicates the current EDI transaction. The EndOfStream property indicates whether the reader is positioned at the end of the stream. The Separators property holds the set of delimiters that were identified for this interchange.

All readers implement IDisposable and should be disposed of either directly or indirectly.

When parsing an EDI file the reader first identifies the separators. When no separators can be found (invalid or missing UNA, UNB or ISA segments) ReaderErrorContext is returned marking the interchange as corrupt. The separators are used to iterate through the segments in the EDI file. On each iteration the reader returns an EdiItem, which can be either a control segment or an EDI transaction.

To parse X12 or HIPAA EDI documents create an instance of X12Reader.

To parse EDIFACT or EANCOM EDI documents create an instance of EdifactReader.

When the EdiItem is an EDI transaction the reader will try to match the version and transaction identifier found in the EDI file to a C# class with the same version and transaction identifier in the MessageAttribute.

The reader searches for matching C# classes in the assembly that was passed in as an input parameter either by name or by load factory.

Assembly is specified by name

var reader = new EdifactReader(ediStream, "EdiFabric.Rules.EDIFACT_D96A")

Assembly is specified by load factory

var reader = new X12Reader(ediStream, AssemblyLoadFactory)
                                
private static Assembly AssemblyLoadFactory(MessageContext messageContext)
{
    //  Resolve by sender
    if (messageContext.SenderId == "PartnerA")
        return Assembly.Load("EdiFabric.Rules.PartnerA");

    //  Resolve by version
    if (messageContext.Version == "004010")
        return Assembly.Load("EdiFabric.Rules.X12004010");

    throw new System.Exception(string.Format("Unsupported version {0}" messageContext.Version));
}

The assembly name is the name of the DLL file which contains the C# types for the EDI transactions.

MessageContext

The message context contains useful information to identify the transaction type, version and the sender or receiver.

EdiReader can be created with an explicit type load factory, which loads the exact .NET type to be used. This mode skips over matching MessageAttribute values and provides a better overall performance. You can use a type load factory to parse different versions of the same EDI transaction to a single C# type.

X12

var ediReader = new X12Reader(ediStream, TypeFactory)

private static TypeInfo TypeFactory(ISA isa, GS gs, ST st)
{
    if(st.TransactionSetIdentifierCode_01 == "810")
        return typeof(TS810).GetTypeInfo();

    throw new Exception("Unsupported transaction.");
}

EDIFACT

var ediReader = new EdifactReader(ediStream, TypeFactory)

private static TypeInfo TypeFactory(UNB unb, UNG ung, UNH unh)
{
    if(unh.MessageIdentifier_02.MessageType_01 == "INVOIC")
        return typeof(TSINVOIC).GetTypeInfo();

    throw new Exception("Unsupported transaction.");
}

EdiReader do not throw exceptions but instead returns ReaderErrorContext when the EDI stream is corrupt and can't be read. All EDI transactions are parsed until the reader can match EDI segments from the EDI file to their corresponding EDI C# POCOs in the EDI template.

Upon reaching an unrecognizable or an improperly positioned segment, the parsing of the current EDI transaction stops and the ErrorContext of EdiMessage is populated with the relevant error details. HasErrors property of each EdiMessage indicates if the message was fully (HasErrors = false) or partially (HasErrors = true) parsed.

var readerErrors = ediItems.OfType<ReaderErrorContext>();
if (readerErrors.Any())
{
    //  The stream is corrupt. Reject it and report back to the sender
    foreach(var readerError in readerErrors)
    {
        //  Respond with the error context, which contains the standard EDI error code and fault reason
        var error = readerError.MessageErrorContext.Flatten();
    }
}

EdiReader supports a Continue-On-Error mode where parsing always continues towards the end of the EDI stream regardless of any errors. To enable it set the boolean ContinueOnError input parameter to true.

Read to end

Reads all control segments and EDI transaction from the start to the end of the EDI stream. This mode loads all EDI data in memory together with the EDI DOM objects and should only be used to read reasonably sized EDI files (up to 3 MB).

var ediStream = File.OpenRead(@"C:\x12.edi");
List<EdiItem> ediItems;
using(var reader = new X12Reader(ediStream, "EdiFabric.Rules.X12004010"))
    ediItems = reader.ReadToEnd().ToList();    

Read one item at a time

Reads one EdiItem at a time. This mode can be used to process EDI files containing multiple EDI interchanges or batches of EDI transactions.

X12

var ediStream = File.OpenRead(@"C:\x12.edi");
using(var reader = new X12Reader(ediStream, "EdiFabric.Rules.X12004010"))
{
    while(reader.Read())
    {
        ISA isa = ediReader.Item as ISA;
        if (isa != null)
        {
            //  Handle isa downstream
        }

        GS gs = ediReader.Item as GS;
        if (gs != null)
        {
            //  Handle gs downstream
        }

        TS810 invoice = ediReader.Item as TS810;
        if(invoice != null)
        {
            //  Handle invoice downstream
        }

        TS850 purchaseOrder = ediReader.Item as TS850;
        if (purchaseOrder != null)
        {
            //  Handle purchaseOrder downstream
        }
    }
}

EDIFACT

var ediStream = File.OpenRead(@"C:\edifact.edi");
using(var reader = new EdifactReader(ediStream, "EdiFabric.Rules.EDIFACT_D96A"))
{
    while(reader.Read())
    {
        UNB unb = ediReader.Item as UNB;
        if (unb != null)
        {
            //  Handle unb downstream
        }

        UNG ung = ediReader.Item as UNG;
        if (ung != null)
        {
            //  Handle ung downstream
        }

        TSINVOIC invoice = ediReader.Item as TSINVOIC;
        if(invoice != null)
        {
            //  Handle invoice downstream
        }

        TSORDERS purchaseOrder = ediReader.Item as TSORDERS;
        if (purchaseOrder != null)
        {
            //  Handle purchaseOrder downstream
        }
    }
}