Getting Started with X12Reader

Requirements:
IPWorks EDI

Introduction

IPWorks EDI and IPWorks EDI Translator include components for reading, and writing X12 documents. This article will focus on using the X12Reader component to parse EDI (X12) documents.

Contents

  1. Loading a Schema
  2. Parsing the Document
  3. Document Navigation

Loading a Schema

The X12Reader component is designed to work with multiple schema formats. A schema allows the component to validate the EDI document while parsing and provide additional details about the structure of the document, such as loops. Multiple schema formats are supported:

If the document type is known ahead of time the LoadSchema method may be called before loading the file. For instance:

//Load the X12 810 schema string schemaFile = @"C:\Schemas\x12_schemas\00401\00401_810.json"; X12reader x12reader = new X12reader(); x12reader.SchemaFormat = X12readerSchemaFormats.schemaJSON; x12reader.LoadSchema(schemaFile);

If the document type is not known ahead of time, then the appropriate schema can be determined when ParseFile is called. In this case the ResolveSchema event will fire with information about the TransactionCode and StandardVersion which may then be used to call LoadSchema from within the ResolveSchema event. For instance:

//Load the X12 810 schema from within the ResolveSchema event. x12reader.OnResolveSchema += (s, e) => { Console.WriteLine("Standard Version: " + e.StandardVersion); Console.WriteLine("Transaction Code: " + e.TransactionCode); if (e.StandardVersion == "004010" && e.TransactionCode == "810") //Load the 810 schema x12reader.LoadSchema(schemaFile); }; x12reader.InputFile = x12_810_File; x12reader.Parse();

Once a schema is loaded and the document is parsed the XPath properties and methods of the component can be used to navigate the document and get information.

Parsing the Document

EDI data may be parsed by either calling ParseFile to parse an existing file, or calling Input to parse a string. As a document is parsed various events will fire with information about the current progress. These events include:

  • StartInterchange
  • StartFunctionalGroup
  • StartTransaction
  • Segment
  • StartLoop
  • EndLoop
  • EndTransaction
  • EndFunctionalGroup
  • EndInterchange

During parsing the component will validate the document. If validation fails the Warning event will fire with details about the error. Please see the documentation of the Warning event for more information.

After the document is parsed the XPath property can be set to navigate within the document. Additionally the methods HasXPath and TryXPath can be used to check the existence of an XPath or navigate to an XPath only if it exists. For example:

x12reader.XPath = "/IX[1]/FG[1]/TX[2]/N1Loop1[1]/N1[1]";

This example path means the following: Select the first N1 segment within the first iteration of the N1Loop1, within second transaction in the first functional group and interchange.

You can also make use of XPath conditional statements to locate the first element which matches a name=value. For example, you could use the following XPath to locate the path of the first element within any N1Loop1 that has a name=N101 and value=BT:

x12reader.XPath = "IX[1]/FG[1]/TX[1]/N1Loop1[N101='BT']";

Note that the conditional statements will search the children, but not the grand children of the element on which the conditional statement is applied. For instance in the above example the children of N1Loop1 will be searched, but the grandchildren will not.

XPath can be set to an absolute path (begins with '/') or a relative path to the current XPath location. The following are possible values for an element accessor:

IXRefers to the Interchange (root) node
FGRefers to a Functional Group node
TXRefers to a Transaction Set node
'name'The first segment or loop of the current container with the given schema name
name[i]The i-th segment of the current container with the given schema name
[i]The i-th segment of the current container
[last()]The last segment of the current container
[last()-i]The segment located at the last location minus i in the current container
..The parent of the current container

After setting XPath the following properties are populated to provide information about the selected path:

XChildren The number of children of the current XPath
XElements A collection of values representing the elements of the segment
XSegment The name of the segment
XSegmentNumber The number of the segment
XSegmentType The type of the segment (Transaction, Segment, Loop, etc.)
XTag The tag of the current segment
XTransactionCode The transaction code of the current segment

Basic XPath Usage

This section provides some basic examples of working with XPath and related properties to navigate and obtain information from a document. This section uses the following X12 810 document:

ISA*00* *00* *ZZ*ACME *ZZ*WAYNE_TECH *160707*1544*U*00401*000000006*0*T*>~ GS*IN*ACME*WAYNE_TECH*20160707*1544*6*T*004010~ ST*810*0001~ BIG*20150708*3003014445**0476553272***DR~ CUR*SE*USD~ REF*8M*0056~ N1*BY*Company*92*544380~ N3*Address~ N4*City*CA*Postal Code~ N1*ST*Name*92*0607047800010~ N3*Address~ N4*City**200131*US~ N1*RE*Name*92*5095956~ N3*Address~ N4*City*IL*Postal Code~ IT1*20*2500*EA*36.96**BP*335S0594~ REF*KK*0099778154~ REF*PO*0476553272*20~ TDS*9240000~ CTT*1~ SE*19*0001~ GE*1*6~ IEA*1*000000006~

In some cases the document structure is known ahead of time and a XPath value can be easily created. However, in other cases it's useful to see the schema structure of the document to get information about what XPath values may exist. To display schema information from the component call the DisplaySchemaInfo method. For instance:

x12reader.LoadSchema(schemaFile); Console.WriteLine(x12reader.DisplaySchemaInfo());

This will output a schema structure like:

ST[0,1] BIG[0,1] NTE[0,100] CUR[0,1] REF[0,12] YNQ[0,10] PER[0,3] N1Loop1[0,200] N1[0,1] N2[0,2] N3[0,2] N4[0,1] REF_2[0,12] PER_2[0,3] DMG[0,1] ITD[0,999999] DTM[0,10] FOB[0,1] PID[0,200] MEA[0,40] PWK[0,25] PKG[0,25] L7[0,1] BAL[0,999999] INC[0,1] PAM[0,999999] LMLoop1[0,10] LM[0,1] LQ[0,100] N9Loop1[0,1] N9[0,1] MSG[0,10] V1Loop1[0,999999] V1[0,1] R4[0,999999] DTM_2[0,999999] FA1Loop1[0,999999] FA1[0,1] FA2[0,999999] IT1Loop1[0,200000] IT1[0,1] CRC[0,1] QTY[0,5] CUR_2[0,1] IT3[0,5] TXI[0,10] CTP[0,25] PAM_2[0,10] MEA_2[0,40] PIDLoop1[0,1000] PID_2[0,1] MEA_3[0,10] PWK_2[0,25] PKG_2[0,25] PO4[0,1] ITD_2[0,2] REF_3[0,999999] YNQ_2[0,10] PER_3[0,5] SDQ[0,500] DTM_3[0,10] CAD[0,999999] L7_2[0,999999] SR[0,1] SACLoop1[0,25] SAC[0,1] TXI_2[0,10] SLNLoop1[0,1000] SLN[0,1] DTM_4[0,1] REF_4[0,999999] PID_3[0,1000] SAC_2[0,25] TC2[0,2] TXI_3[0,10] N1Loop2[0,200] N1_2[0,1] N2_2[0,2] N3_2[0,2] N4_2[0,1] REF_5[0,12] PER_4[0,3] DMG_2[0,1] LMLoop2[0,10] LM_2[0,1] LQ_2[0,100] V1Loop2[0,999999] V1_2[0,1] R4_2[0,999999] DTM_5[0,999999] FA1Loop2[0,999999] FA1_2[0,1] FA2_2[0,999999] TDS[0,1] TXI_4[0,10] CAD_2[0,1] AMT[0,999999] SACLoop2[0,25] SAC_3[0,1] TXI_5[0,10] ISSLoop1[0,999999] ISS[0,1] PID_4[0,1] CTT[0,1] SE[0,1]

To output a representation of the current file call the DisplayXMLInfo method after calling ParseFile. Calling DisplayXMLInfo will return the structure of the current document represented as XML. For instance:

x12reader.LoadSchema(schemaFile); Console.WriteLine(x12reader.DisplayXMLInfo());

Will output a string like:

This information provides a starting place if there is no prior knowledge about the document structure.

Note: If XML translation is desired a separate EDI Translator component is included in the toolkit specifically for that task.

When setting XPath, examining the current segment's elements is a common task. To select this segment from the example document:

BIG*20150708*3003014445**0476553272***DR~

The following code can be used:

x12reader.XPath = "/IX/FG/TX/BIG"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].Value); }

Which will output:

BIG01: 20150708 BIG02: 3003014445 BIG03: BIG04: 0476553272 BIG05: BIG06: BIG07: DR

The XElements[i].DataType property provides information about the element's data type which may be useful for interpreting the value. For instance:

x12reader.XPath = "/IX/FG/TX/BIG"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + "[" + x12reader.XElements[i].DataType + "]: " + x12reader.XElements[i].Value); }

Outputs:

BIG01[DT]: 20150708 BIG02[AN]: 3003014445 BIG03[DT]: BIG04[AN]: 0476553272 BIG05[AN]: BIG06[AN]: BIG07[None]: DR

Possible DataType values are:

AN AlphaNumeric
ID Identifier; allowed values might be defined by the transaction set schema
N Numeric
R Floating-point number
DT DateTime
TM Time
None Type is unknown or not provided by the schema
Composite This element has multiple components

Using Schema Information

The X12Reader component provides Element specific fields which provide additional information about the element. This additional information includes the element name as taken from the Schema Id, and a textual description of the element. The following fields provide information obtained from the schema:

  • XElements.SchemaName
  • XElements.SchemaDesc
  • XElements.ComponentSchemaName
  • XElements.ComponentSchemaDesc

Name holds positional (ref) value like "N101". SchemaName holds the Id taken from the schama.

For instance:

x12reader.XPath = "/IX/FG/TX/BIG"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].SchemaName + "[" + x12reader.XElements[i].SchemaDesc + "]: " + x12reader.XElements[i].Value); }

When SchemaName and SchemaDesc are output this will use the name and description from the schema and will result values like:

373[Date]: 20150708 76[Invoice Number]: 3003014445 373[Date]: 324[Purchase Order Number]: 0476553272 328[Release Number]: 327[Change Order Sequence Number]: 640[Transaction Type Code]: DR

In contrast, if Name was used the output would look like:

BIG01: 20150708 BIG02: 3003014445 BIG03: BIG04: 0476553272 BIG05: BIG06: BIG07: DR

Note: These fields are only applicable when a JSON schema is loaded.

Working with Loops

Within this particular document there are multiple N1 Loops representing the buying party (BY), the party to receive remittance (RE) and the party to which the shipment will go (ST). This is the section of the document holding these values:

N1*BY*Company*92*544380~ N3*Address~ N4*City*CA*Postal Code~ N1*ST*Name*92*0607047800010~ N3*Address~ N4*City**200131*US~ N1*RE*Name*92*5095956~ N3*Address~ N4*City*IL*Postal Code~

To select the first instance of the N1 Loop set:

x12reader.XPath = "/IX/FG/TX/N1Loop1[1]"; Console.WriteLine("Elements: " + x12reader.XElements.Count); Console.WriteLine("Children: " + x12reader.XChildren); Console.WriteLine("Segment: " + x12reader.XSegment); Console.WriteLine("Segment Type: " + x12reader.XSegmentType);

Which outputs:

Elements: 0 Children: 3 Segment: N1Loop1 Segment Type: stTransactionLoop

In this case there are no elements because this is the XPath to the first instance of the N1 Loop itself. To access the individual segments within the loop the child segments of the loop must be accessed. For instance:

x12reader.XPath = "/IX/FG/TX/N1Loop1[1]/N1"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].Value); }

Outputs:

N101: BY N102: Company N103: 92 N104: 544380

Accessing other instances of the loop can be done simply by incrementing the index used N1Loop[1] part of the XPath. For instance:

x12reader.XPath = "/IX/FG/TX/N1Loop1[3]/N1"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].Value); }

Outputs:

N101: RE N102: Name N103: 92 N104: 5095956

To select a specific N1 Loop based on the value of one of the elements within the loop, a conditional XPath can be specified. For instance if the desired N1 Loop contains:

N1*ST*Name*92*0607047800010~

The following code can be used to select the specific N1 Loop where the party is the shipment destination (ST):

x12reader.Config("ResolveXPathOnSet=true"); x12reader.XPath = "/IX/FG/TX/N1Loop1[N101='ST']"; Console.WriteLine(x12reader.XPath);

Note the conditional part of the XPath N1Loop1[N101='ST'] uses the element id and desired element value. When using the JSON schemas the element may also be referred by its name as well (in this case "98"). For instance:

x12reader.XPath = "/IX/FG/TX/N1Loop1[98='ST']";

The configuration setting ResolveXPathOnSet is used so when querying the XPath property, a resolved XPath without the original conditional statement is returned. This code will output:

/IX[1]/FG[1]/TX[1]/N1Loop1[2]

Now that the desired loop XPath is known, it may be used in the construction of additional XPath values. Alternatively, the conditional statement may be included as part of an XPath to a specific child. For instance, to both select the N1 Loop where for the shipment destination (ST) and access the N4 segment within the loop, the following code can be used:

x12reader.XPath = "/IX/FG/TX/N1Loop1[N101='ST']/N4"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].Value); }

Which outputs:

N401: City N402: N403: 200131 N404: Country

HasXPath and TryXPath

The component includes a HasXPath method which can be used to determine if a specific XPath exists before attempting to navigate to it. In addition, the TryXPath method can be used to navigate to the XPath only if it exists. TryXPath returns True if the navigation was successful, False otherwise.

In the test document there is one IT1Loop:

IT1*20*2500*EA*36.96**BP*335S0594~

These methods can be used to detect how many instances of the loop exist and navigate accordingly. For instance, here are two different approaches that both output the elements for each IT1 Loop in the document

//Use HasXPath to check for the XPath before navigating int index = 1; while (x12reader.HasXPath("/IX/FG/TX/IT1Loop1[" + index + "]")) { Console.WriteLine("***** Start IT1Loop1 *****"); x12reader.XPath = "/IX/FG/TX/IT1Loop1[" + index + "]/IT1"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].Value); } index++; Console.WriteLine("***** End IT1Loop1 *****"); } //Use TryXPath to try navigating int index = 1; while (x12reader.TryXPath("/IX/FG/TX/IT1Loop1[" + index + "]/IT1")) { Console.WriteLine("***** Start IT1Loop1 *****"); for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].Value); } index++; Console.WriteLine("***** End IT1Loop1 *****"); }

Both approaches will output the same values:

***** Start IT1Loop1 ***** IT101: 20 IT102: 2500 IT103: EA IT104: 36.96 IT105: IT106: BP IT107: 335S0594 ***** End IT1Loop1 *****

Composite and Repeat Elements

Composite and Repeat elements are identified by the component when examining the element properties.

Composite Elements

The XElement[i].Composite* properties hold information about the components of the composite element. Below is an example 846 document that contains a composite element in the QTY segment.

ISA*00* *00* *14*060704780001900*ZZ*COMPANYTEST *140902*1433*U*00401*000040157*1*T*:~ GS*IB*060704780500*TESTEDI*20130428*2033*000040157*X*004010~ ST*846*0001~ BIA*00*01*5000412255*20090320~ DTM*050*20090320*043510~ REF*PO*0476401324~ REF*DD*Document Identification Code~ REF*D2*123456789~ N1*16*Operations Center*6*LU10~ N4*Daventry***GB~ LIN*00110*MG*M9057Z/A~ PID*F****COMPANYWORKS~ QTY*87*300*SM:2:3:28~ CTT*1~ SE*13*0001~ GE*1*000040157~ IEA*1*000040157~

In this document, the QTY segment includes a composite element with the : separator:

QTY*87*300*SM:2:3:28~

When inspecting the elements of the QTY segment the XElements[i].DataType will indicate the type of element. In this case DataType will be "Composite". After identifying the composite element the XElements[i].ComponentIndex can be set to iterate over the components from 0 to XElements[i].ComponentCount. Setting XElements[i].ComponentIndex populates the XElements[i].ComponentType, XElements[i].ComponentName, and XElements[i].ComponentValue properties. For instance:

x12reader.XPath = "/IX/FG/TX/LINLoop1[1]/QTYLoop1[1]/QTY"; for (int i = 0; i < x12reader.XElements.Count; i++) { Console.WriteLine(x12reader.XElements[i].Name + ": " + x12reader.XElements[i].DataType + ": " + x12reader.XElements[i].Value); if (x12reader.XElements[i].DataType == "Composite") { //Loop through the composite element components. for (int compIdx = 0; compIdx < x12reader.XElements[i].ComponentCount; compIdx++) { x12reader.XElements[i].ComponentIndex = compIdx; Console.WriteLine(" Component " + compIdx + ": " + x12reader.XElements[i].ComponentValue); } } }

Outputs:

QTY01: None: 87 QTY02: R: 300 QTY03: Composite: SM:2:3:28 Component 0: SM Component 1: 2 Component 2: 3 Component 3: 28

Repeat Elements

The XElement[i].RepeatCount property indicates the number of time this element is repeated in the segment. If the element is repeated set XElement[i].RepeatIndex to a value from 0 to RepeatCount to select an instance of the repeated element and examining that instance's properties. For instance if the EDI document contains a segment:

TST*TEST*1^4^8~

In this case the element at TST02 is repeated using the ^ delimiter. The following code:

x12reader.XPath = "/IX/FG/TX/TST"; for (int j = 0; j < x12reader.XElements[1].RepeatCount - 1; j++) { x12reader.XElements[1].RepeatIndex = j; Console.WriteLine("Value: " + x12reader.XElements[1].Value); }

Will output:

Value: 1 Value: 4 Value: 8

We appreciate your feedback.  If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.