logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·27.10.26
← Back to news

Parsing EDIFACT in Python with syntax trees

Original article
github.com|14 points|1 comments|by alexprengere|Oct 22, 2026

Edison: Advanced EDIFACT Parsing in Python

AmadeusITGroup Edison Build Version Python versions License Downloads

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.

Edison Logic


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:

FieldDefaultDescription
TAGRequiredThe 3-character EDIFACT segment identifier.
children[]A list of nested segment nodes.
actionNoneThe name of the method to call upon parsing.
repetition999991 for single-use; >1>1>1 for repeatable segments.
flagNoneAdditional configuration flags.

API Reference

Primary Methods

  • parser.parse(edi, version=None): Processes the message and triggers callbacks.
  • Parser.validate(edi, version=None): Returns True if 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 a pydot.Dot graph of the state machine.

Utility Functions

  • extract_envelope_headers(edi): Retrieves UNB and UNH segments.
  • extract_envelope_footers(edi): Retrieves UNT and UNZ segments.
  • 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:

Selected Schema=max⁡(Schema Version≤Requested Version)\text{Selected Schema} = \max(\text{Schema Version} \le \text{Requested Version})Selected Schema=max(Schema Version≤Requested Version)

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 version argument 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 a ValueError.

Constraints and Repetition

The repetition field determines if the state machine can loop back to the same node.

  • Repeatable: Any value >1>1>1.
  • 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.

Transition Graph