edifact

package module
v0.0.0-...-f92b857 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2025 License: MIT Imports: 4 Imported by: 0

README

Edifact - specify and read arbitrary edifact document formats

A Golang module to specify edifact document formats and to read from io.Reader into user-defined data structures. Inspired by json.Unmarshal and xml.Unmarshal.

Installation

Install with go get

go get github.com/shogg/edifact

Document specification

Document specifications are written as go source code: Msg defines a message, S a segment and SG a segment group (a loop). "UNA" is a segment tag. C and M stand for conditional or mandatory. The last number specifies maximal repetitions.

The notation is derived from here (example): https://service.unece.org/trade/untdid/d96a/trmd/desadv_s.htm

import "github.com/shogg/edifact/spec"

var desadv = spec.Msg("DESADV",
	spec.S("UNA", spec.C, 1),
	spec.S("UNB", spec.C, 1),
	spec.S("UNH", spec.M, 1),
	spec.S("BGM", spec.M, 1),
	spec.S("DTM", spec.C, 10),
	spec.S("ALI", spec.C, 5),
	spec.S("MEA", spec.C, 5),
	spec.S("MOA", spec.C, 5),
	spec.SG("SG1", spec.C, 10,
		spec.S("RFF", spec.M, 1),
		spec.S("DTM", spec.C, 1),
	),
[..]

A specification must be registered. This can be done anywhere. For instance in the main function or in an init function:

func init() {
	spec.Add(desadv)
}

User defined data structures

Currently only struct is supported (and array, slice and pointer of struct). The tag of a struct field specifies where data is located in an edifact document. All annotated information must match to make segment data suitable (segment groups, segment tag, fixed data in elements). A question mark ? annotates the relevant data element separated by :. The type of the target value decides how to parse. Simple data types are implemented, more complex ones need to implement edifact.Marshaller. A star * annotates a composite of elements, an edifact.Marshaller is needed to parse. If no ? or * is present, the edifact.Marshaller will receive the full segment with tag and terminator.

Arrays and slices in a struct can specify a segment group path like this edifact:"SG10/SG17". Each time a segment group repeats in the edifact data a new array/slice element is inserted.

type Message struct {
	Date       time.Time `edifact:"DTM+17|18"`
	DeliveryNr string    `edifact:"SG1/RFF+VN|DQ+?"`
	OrderNr    int       `edifact:"SG1/RFF+ON+?"`
	Items      []Item    `edifact:"SG10/SG17"`
}
type Item struct {
	ItemNr      int    `edifact:"SG10/SG17/LIN+?"`
	Description string `edifact:"SG10/SG17/LIN+++?"`
	Quantity    int    `edifact:"SG10/SG17/QTY+12:?"`
}

edifact.Unmarshaller

Out of the box only simple data types (string, int, bool ... ) and time.Time are parseable. A user defined type can implement edifact.Unmarshaller to provide its own parsing.

// Unmarshaller interface for custom data type parsing.
type Unmarshaller interface {
	UnmarshalEdifact(data []byte) error
}

Read edifact documents

The top level API is edifact.Unmarshal. You provide an io.Reader and a pointer to your data structure to be filled. Here is a complete example. It uses the built-in document specification "DESADV" so no doc spec here:

package main

import (
	"fmt"
	"strings"
	"time"

	"github.com/shogg/edifact"
)

var ediMessage = `
UNA:+.? '
UNB+UNOC:3+sender+receiver+060620:0931+1++1234567'
UNH+1+DESADV:D:96A:UN'
BGM+220+B10001'
DTM+17:20060620:102'
RFF+ON+1'
RFF+DQ+2'
NAD+BY+++name+street+city++23436+xx'
CPS+'
LIN+1++product A:SA'
QTY+12:10'
LIN+2++product B:SA'
QTY+12:20'
CNT+2:1'
UNT+9+1'`

type Message struct {
	Date       time.Time `edifact:"DTM+17|18"`
	DeliveryNr string    `edifact:"SG1/RFF+VN|DQ+?"`
	OrderNr    int       `edifact:"SG1/RFF+ON+?"`
	Items      []Item    `edifact:"SG10/SG17"`
}
type Item struct {
	ItemNr      int    `edifact:"SG10/SG17/LIN+?"`
	Description string `edifact:"SG10/SG17/LIN+++?"`
	Quantity    int    `edifact:"SG10/SG17/QTY+12:?"`
}

func main() {
	document := strings.NewReader(ediMessage)
	var messages []*Message
	if err := edifact.Unmarshal(document, &messages); err != nil {
		panic(err)
	}
	fmt.Println("number of messages", len(messages))
}

Issues

  • meta characters are fixed to UNA:+.? '
  • UNA, UNB don't get evaluated
  • time.Time can only parse DTM formats 101, 102, 201-204
  • only ORDERS and DESADV are included atm but you can register your own specs
  • multiple message format versions are not supported atm
  • max. segment repetitions is part of the spec but is ignored atm

Contributions are welcome.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Release

func Release(s string) string

Release add '?' in front of meta characters ':', '+', '\” to release them.

func Unmarshal

func Unmarshal(r io.Reader, target interface{}) error

Unmarshal edifact document into target data structure.

Types

type Unmarshaller

type Unmarshaller interface {
	UnmarshalEdifact(data []byte) error
}

Unmarshaller interface for custom data type parsing.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL