Read EDI documents

Reading EDI documents allows you to access the data they contain as typed objects, which are instances of their respective EDI Spec classes.

ediFabric reads byte by byte from the incoming stream and probes for interchange headers, ISA or UNA\UNB, depending on the standard. Having found a match, it will then probe it further according to its structure, until a valid segment terminator is found. ISA and UNA segments contain all of the separators used in the envelope. When no UNA is present, the default separators for Edifact will be loaded.

Once a valid interchange header was found and a valid set of separators was loaded the parsing of the document begins. ediFabric supports multiple interchanges in a file therefore it will always probe for interchange header and will load new separators if one is found. This allows not only to read files with multiple interchanges but also multiple interchanges with different separators each.

The reader will skip over any blank spaces, carriage return or line feeds (when none of these is used as a segment separator) found before the interchange header, between the segments and after the interchange trailer.

The reader will try to find a valid interchange header and will skip over all data until a valid one is found. It reads segment by segment from the stream, until a segment terminator is found, therefore there is only one segment worth of data loaded in the memory at any time. In case a segment terminator can't be found in the first 5000 characters, the search for segment will stop and the accumulated memory will be released. The interchange will be marked as corrupt and the reader will continue searching for the next interchange.

EDI documents needs to be converted into streams prior to be read. This is usually trivial as they are distributed as files or streams anyway.

There are two readers available - X12Reader for reading X12 or Hipaa documents and EdifactReader for reading Edifact or Eancom documents.

The constructor always takes at least two parameters - the EDI document stream and the assembly (or its name) containing the EDI spec classes.

Create reader with assembly name

var ediStream = File.OpenRead(@"C:\edi.txt");
var reader = new X12Reader(ediStream, "YourEdiSpecsProject")

Create reader with delegate

var ediStream = File.OpenRead(@"C:\edi.txt");
var reader = new X12Reader(ediStream, AssemblyLoadFactory);
                                    
private static Assembly AssemblyLoadFactory(MessageContext messageContext)
{
    if (messageContext.SenderId == "PartnerA")
        return Assembly.Load("EdiFabric.Rules.PartnerA.X12002040");

    return Assembly.Load("EdiFabric.Rules.X12002040");
}

The assembly name can be found in Visual Studio by right-clicking on the project that contains the EDI Spec C# files:

Visual Studio assembly name

The rule of the thumb is - use assembly name if all of the EDI Specs are stored in a single project and use a delegate if the EDI Specs are stored in multiple assemblies. The delegate can implement a factory to resolve the correct assembly based on the format, version or tag of the EDI document. MessageContext contains the sender and receiver id's and qualifiers from the interchange header, so you can tally those up with partner specific spec projects.

The readers can also be created with custom encoding.

All readers implement IDisposable and have to be disposed of.

There are two reading modes - read to end and read item by item. Read to end reads the stream to the end and loads all items into the memory. Reading item by item only loads the currently read item into the memory.

Read to end

List<IEdiItem> ediItems = reader.ReadToEnd();
                                   
ISA isa = ediItems.OfType<ISA>().Single();
GS gs = ediItems.OfType<GS>().Single();
List<EdiMessage> invoices = ediItems.OfType<TS810>();    
GE ge = ediItems.OfType<GE>().Single();
IEA iea = ediItems.OfType<IEA>().Single();

Internally it uses iterator blocks to yield the results and therefore the result set needs to be iterated before any items are available.

Read item by item

while (ediReader.Read())
{
    IEdiItem currentItem = ediReader.Item as TS810;
}

EdiItem can be any message (transaction set), interchange or group header\trailer, reader error context or message error context.

The reader will not break when a fault occurs. When the error is due to corrupt interchange or group headers, the exception will be wrapped up as a ReaderErrorContext and the parser will continue searching for a valid interchange. When the error is due to a missing or duplicate spec, or unable to load the specs' assembly, an additional MessageErrorContext will be included in the ReaderErrorContext. When an error occurs whilst parsing a message, the parser terminates and returns all of the successfully parsed segments up to the point of termination. In this case the parsed message will also include a MessageErrorContext with the failure details. This ensures that all available information in a file can be extracted even when some of the data is corrupt.

All messages are partially parsed. This means that regardless of whether the parsing was successful, the reader will always return the target typed object for the transaction being read. Every transaction typed object (the instance of the class attributed with MessageAttribute) inherits from EdiMessage which has a MessageErrorContext property. When a segment from the EDI document can't be parsed for whatever reason, the MessageErrorContext will be populated with the details such as the position of the segment and the reason for failure. All of the previously parsed segments will be still available, hence the partial parsing. When the parsing was successful the MessageErrorContext will be null.

Check if a transaction was read correctly

while (ediReader.Read())
{
    IEdiItem currentItem = ediReader.Item as TS810;
    if (currentItem != null) 
    {
        // check for errors as messages may have been partially parsed
        if (currentItem.HasErrors)
        {
        } 
    }
}