Parsing EDIFACT in Python with syntax trees
Edison: Advanced EDIFACT Parsing in Python
Edison is a specialized Python library designed to parse EDIFACT messages. Unlike traditional parsers that treat a message as a simple, flat sequence of segments, Edison utilizes real syntax trees.
Core Philosophy
Edison models EDIFACT messages as recursive structures. In this model, segments can act as parents to groups of child segments. This hierarchical approach allows for a more nuanced representation of the data.
Key Technical Capabilities:
- State Machine Logic: The library converts the defined syntax tree into a state machine to manage valid transitions between segments.
- Flexible Segment Groups: Supports child segments that are mandatory, conditional, or repeatable.
- Robust Tokenization: Handles EBCDIC control characters and escaped EDIFACT separators.
- Specialized Handling: Correctly processes segments with fixed lengths (e.g.,
BLB). - Visualization: Ability to export transition graphs using
pydot.

Getting Started
- Install the package via pip
- Create a subclass of
BaseParser - Define the message schema
- Implement callback methods for data processing
Installation:
pip install edison-parser
Quick Start Example
To use Edison, you define a schema and link specific segment tags to "action" methods (callbacks).
from edison import BaseParser
class PIZZAQParser(BaseParser):
# Schema keyed by version (major, minor)
schemas = {
(0, 0): [
["UNH"],
["CVR"],
["REF"], # Standard REF segment
["PIZ", [
["TYP", [], "parse_typ"],
["REF", [], "parse_ref"], # Nested REF segment
]],
["UNT"],
],
}
def __init__(self):
self.pizza_orders = {}
def parse_typ(self, segment: str):
# Triggered when "TYP" is encountered
self.current_pizza = segment[4:]
def parse_ref(self, segment: str):
# Triggered when "REF" is encountered
self.pizza_orders[self.current_pizza] = self.pizza_orders.get(self.current_pizza, 0) + 1
# Sample EDIFACT data
edi_data = """UNH+1+PIZZAQ:1:1:1A+00012480825035'
CVR+USD'
REF+LUIS'
PIZ+1'
TYP+MARGARITA'
REF+1*2'
PIZ+2'
TYP+PESTO'
REF+3'
UNT+19+1'"""
parser = PIZZAQParser()
parser.parse(edi_data)
print(parser.pizza_orders) # Output: {"MARGARITA": 1, "PESTO": 1}
PIZZAQParser.show(edi_data) # Pretty-print the structure
Schema Configuration
A schema is a dictionary where keys are version tuples. Each node in the schema list follows this positional format:
| Field | Default | Description |
|---|---|---|
| TAG | Required | The 3-character EDIFACT segment identifier. |
| children | [] | A list of nested segment nodes. |
| action | None | The name of the method to call upon parsing. |
| repetition | 99999 | 1 for single-use; for repeatable segments. |
| flag | None | Additional configuration flags. |
API Reference
Primary Methods
parser.parse(edi, version=None): Processes the message and triggers callbacks.Parser.validate(edi, version=None): ReturnsTrueif the message adheres to the schema transitions.Parser.show(edi, version=None, colorize=True): Generates a human-readable, indented view of the EDI.Parser.build_dot_object(version=None, include_up_links=True): Creates apydot.Dotgraph of the state machine.
Utility Functions
extract_envelope_headers(edi): RetrievesUNBandUNHsegments.extract_envelope_footers(edi): RetrievesUNTandUNZsegments.escape(data)/unescape(data): Manages separators like+,:,*, and'.normalize_ebcdic_separators(edi): Safely converts EBCDIC separators to ASCII.
Advanced Implementation
Versioned Syntax Trees
Edison allows the same message type to evolve. You can define multiple schemas, and the parser will automatically select the most appropriate one based on the following logic:
class RouteParser(BaseParser):
schemas = {
(0, 0): [
["UNH"],
["ORG", [], "parse_origin"],
["DST", [], "parse_destination"],
["UNT"],
],
(2, 0): [
["UNH"],
["LEG", [
["ORG", [], "parse_origin"],
["DST", [], "parse_destination"],
]],
["UNT"],
],
}
# ... (callback methods parse_origin and parse_destination) ...
Note: If the
versionargument is missing or exceeds known schemas, the latest available schema is used. If the requested version is older than the earliest defined schema,find_transitions()will trigger aValueError.
Constraints and Repetition
The repetition field determines if the state machine can loop back to the same node.
- Repeatable: Any value .
- Single-use: A value of
1.
The parser does not track exact counts (e.g., it cannot distinguish between exactly 2 occurrences vs 99999).
Mandatory Logic: If a node is marked as mandatory (repetition = 1), the parser cannot skip it to reach a subsequent sibling, parent, or child.
Crucial Detail: A mandatory child does not automatically make its parent mandatory. To ensure a whole group is required, the group node itself must be marked as mandatory.
