A Python library for converting between the backend LineageGraph JSON schema and a compact, token-optimized lineage definition format.
Install directly from GitHub:
pip install git+https://github.com/IBM/lineage-data-format.gitPin to a specific release:
pip install git+https://github.com/IBM/lineage-data-format.git@v0.2.0Install from source for development:
git clone https://github.com/IBM/lineage-data-format.git
cd lineage-data-format
pip install -e ".[dev]"Both json_to_lineage_format and convert_json_file_to_lineage expect the backend LineageGraph response format:
{
"assets_in_view": [ /* list of LineageAsset objects */ ],
"edges_in_view": [ /* list of FlowEdge objects */ ],
"graph_calculation_datetime": "2024-07-22T08:16:22.694Z",
"graph_calculation_timestamp": 1721636182694
}| Field | Type | Notes |
|---|---|---|
id |
string | Asset UUID |
name |
string | Display name |
type |
string | e.g. "Column", "Table" |
resource_key |
string | e.g. "PostgreSQL/db/schema/table/col" |
origin |
string | e.g. "runtime" |
technology |
{id, name} |
Technology lookup |
hierarchical_path |
[{id, name, type}] |
Ancestry path |
attributes |
[{name, value}] |
Asset attributes |
tags |
string[] |
Tag list |
business_terms |
[{id, name}] |
|
business_classifications |
[{id, name}] |
|
data_classes |
[{id, name}] |
|
source_code_snippets |
array | Full snippet objects |
catalog_assignments |
array | Full assignment objects |
project_assignments |
array | Full assignment objects |
space_assignments |
array | Full assignment objects |
data_source_definition_asset |
{id, name} |
|
children |
{count, has_any, href} |
Child summary |
descendant_statistics |
{has_leaf_children, is_every_child_leaf, number_of_leaf_descendants} |
Leaf statistics, written as the ds= node token |
is_deduced |
bool | |
is_transforming |
bool | |
is_operational |
bool | |
is_temporary |
bool | |
is_favorite |
bool |
| Field | Type | Notes |
|---|---|---|
source |
string | Source asset UUID |
target |
string | Target asset UUID |
type |
"direct" | "summary" |
Edge type |
from ldf import (
json_to_lineage_format,
lineage_format_to_json,
convert_json_file_to_lineage,
convert_lineage_file_to_json,
)
# Convert a backend LineageGraph dict to LDF string
ldf_text = json_to_lineage_format(backend_response)
# Convert LDF string back to backend LineageGraph dict
restored = lineage_format_to_json(ldf_text)
# Convert files directly
convert_json_file_to_lineage('input.json', 'output.ldf')
convert_lineage_file_to_json('input.ldf', 'output.json')
# Compact mode (single blank line between sections instead of two)
ldf_compact = json_to_lineage_format(backend_response, {'compact': True})
# Legacy, fully expanded output (no alias compression)
ldf_expanded = json_to_lineage_format(backend_response, {'aliases': False})
# Drop every URL, and append a legend of the sections used
ldf_small = json_to_lineage_format(backend_response, {'no_urls': True, 'legend': True})
# Exclude fields from the output (applied to the input, before serialization)
ldf_slim = json_to_lineage_format(
backend_response,
{'exclude_fields': ['children', 'technology.id', 'hierarchical_path.type', 'is_*']},
)A bool in the options dict, True by default. It removes repeated values from
the output (see Alias compression). False writes the
expanded form of version 0.2.0. The decoder reads both forms. Command line:
--no-aliases.
A bool in the options dict, False by default. It drops every URL: the href
field wherever it occurs (children.href and the hrefs inside catalog, project
and space assignments). It is the same as adding "href" to exclude_fields, and
no URL section is written. Command line: --no-urls; file helper:
convert_json_file_to_lineage(..., no_urls=True).
A bool in the options dict, False by default. It adds a LEGEND section as the
first section, with one SECTION=expansion line (e.g. HP=Hierarchical path) for
every abbreviated section present in the output. It only spells the names out; it
sits first so a reader knows the abbreviations before it meets them. The decoder
ignores it, so the round trip is unchanged. Command line:
--legend; file helper: convert_json_file_to_lineage(..., legend=True).
A list of str passed in the options dict. The default is an empty list, in which
case the output is identical to the output without the option.
| Entry | Effect |
|---|---|
"children" |
removes the field with that name at every nesting level (assets, edges, top level, nested objects) |
"technology.id" |
removes only that field; the path is relative to the asset/edge, arrays take no indices |
"technology" |
removing an object or array removes its whole content |
"is_*", "*_assignments" |
wildcards, matched with fnmatch (case-sensitive) |
| unknown entry | ignored (logged at DEBUG level) |
Excluded data leaves no trace in the output: no empty separators, entries or
sections. For example, with technology excluded no path=, T or TID is
written, and with hierarchical_path excluded no HP section is written.
When only part of a record is removed, the remaining fields keep their meaning
through named forms that the decoder understands (chc=/cha=/chh= for
children, i=/n=/t= in HP segments, n=name / bare id in lookups).
A non-list value raises TypeError; removing an asset id or an edge
source/target raises ValueError.
# Convert JSON to lineage format
ldf json-to-lineage input.json output.ldf
# Convert with compact format (minimal blank lines)
ldf json-to-lineage input.json output.ldf --compact
# Expanded output without alias compression
ldf json-to-lineage input.json output.ldf --no-aliases
# Drop every URL (same as --exclude href)
ldf json-to-lineage input.json output.ldf --no-urls
# Add a LEGEND section that spells out the section abbreviations
ldf json-to-lineage input.json output.ldf --legend
# Exclude fields (comma-separated and/or repeated; names, dotted paths, wildcards)
ldf json-to-lineage input.json output.ldf --exclude children,descendant_statistics,is_favorite,source_code_snippets
# Convert lineage format back to JSON
ldf lineage-to-json input.ldf output.json
# Specify JSON indentation
ldf lineage-to-json input.ldf output.json --indent 4The Lineage Definition Format is a compact, plain-text format that defines repeated values once in named lookup sections and references them via short aliases throughout. All sections are optional except GRAPH, START, E, and N.
| Section | Type | Content |
|---|---|---|
GRAPH |
Structural | Marker line + optional dt= / ts= meta |
START |
Structural | Alias of the first asset |
E |
Structural | Directed edges: N1>N2, grouped by source N1>N2,N3; an edge without a target (end edge) is just the source alias: N7 |
EP |
Structural | Edge properties: N1>N2 etype=direct (end edge: N7 etype=summary), listed in E order; *>* etype=X / * etype=Y are the defaults |
NDEF |
Defaults | Node tokens shared by most nodes: origin=design path=T1 flags=7 |
IKP |
Lookup | identity_key prefixes: P1=<encoded prefix> |
N |
Structural | Node definitions (see format below) |
T |
Lookup | Technology names: T1=PostgreSQL |
TID |
Lookup | Technology UUIDs: T1=<uuid> |
ID |
Lookup | Node UUIDs: N1=<uuid> |
URL |
Lookup | URL templates and prefixes: U1=<template or prefix>, used as @U1 |
SRC |
Lookup | Source code ids shared by compact SCS entries: S1=<source_code id> |
NAME |
Lookup | Node display names: N1=first_name |
BT |
Lookup | Business terms: BT1=<uuid>|Term Name |
BC |
Lookup | Business classifications: BC1=<uuid>|Name |
DC |
Lookup | Data classes: DC1=<uuid>|Name |
HP |
Per-node | Hierarchical path: N1=id:name:type|… |
ATTR |
Per-node | Asset attributes: N1=name:value|… |
SCS |
Per-node | Source code snippets: compact N1=<id>@S1@41:1-44:38 or JSON N1=[…] |
CA |
Per-node | Catalog assignments (JSON): N1=[…] |
PA |
Per-node | Project assignments (JSON): N1=[…] |
SA |
Per-node | Space assignments (JSON): N1=[…] |
DSD |
Per-node | Data source definition asset: N1=id:name |
REF |
Lookup | Shared values of HP / ATTR / DSD: R1=<value>, used as @R1 |
LEGEND |
Informational | Optional (legend / --legend), always first: one SECTION=expansion line per abbreviated section present (GRAPH and START are not listed). The decoder ignores it |
With aliases on, repeated values are written once:
| Construct | Example | Meaning |
|---|---|---|
REF + @Rn |
R1=TRANSFORMATION_TAG=TRANSFORMING, entry N1=@R1|@R2 |
a whole entry value or one |-separated element is replaced by the reference; a reference may refer to other references. An @… that is not defined in REF is a literal |
* entry |
HP: *=<value>, N3=<other>, N4= |
default value of HP / DSD. A node with its own entry keeps it; an empty entry (N4=) means none |
IKP |
P1=uuid/dsd%5BDatabase%5D/, node ikp=P1 ik=Asset%201… |
identity_key = prefix + ik |
NDEF |
origin=design flags=7 |
tokens inherited by every node. A node's own token overrides it, an empty one (origin=) removes it; flags=0 is written explicitly |
URL + @Un |
U1=https://…/lineage_assets/{id}/children, node token ch=0,0,@U1 |
children.href template; {id} is the node UUID from ID. In CA / PA / SA blobs a shared href prefix is written @U2/rest; a literal href that starts with @ is written @@… |
SRC + compact SCS |
S1=<source_code id>, entry N5=<snippet id>@S1@41:1-44:38 |
<snippet id>@<source alias>@<row>:<col>-<row>:<col>; several snippets of one node are joined by |. A node whose snippets do not fit this form keeps the JSON entry |
grouped E |
N1>N2,N3 |
all targets of one source on one line |
default EP |
*>* etype=direct, * etype=summary |
type of edges with a target / of end edges; N1>N2 etype=X overrides it, etype= means no type |
The text is smaller, the data is the same: assets and the set of edges (with their types) survive a roundtrip. The order of the edge list does not: sources keep their first-occurrence order and targets keep their original order. A section is written only when it saves space.
N1:Column rk=PostgreSQL/db/s1/t/col origin=runtime path=T1 tags=[PII] bt=[BT1] bc=[BC1] dc=[DC1] flags=0 ch=0,0,https://…/children
| Token | Meaning |
|---|---|
N1:Column |
alias:type |
rk=… |
resource_key |
origin=… |
origin |
path=T1 |
technology alias (→ T / TID lookup) |
tags=[…] |
comma-separated tag list |
bt=[…] |
business term aliases |
bc=[…] |
business classification aliases |
dc=[…] |
data class aliases |
flags=N |
bitmask: bit0=is_deduced, bit1=is_transforming, bit2=is_operational, bit3=is_temporary, bit4=is_favorite; omitted when 0 |
ch=count,has_any,href |
children summary; comma-separated to avoid conflicts with URLs. With aliases on href is @Un, a template from the URL section |
ds=a,b,n |
descendant_statistics: has_leaf_children, is_every_child_leaf (0/1) and number_of_leaf_descendants. Partial forms dsl=, dse=, dsn= are written when a field was excluded; dsj=<json> holds unusual values |
See examples/output_backend.ldf for the full LDF representation of examples/mock_backend_response.json.
- Token-optimized: Reduces token count by 4–6× compared to JSON
- Bidirectional / lossless: Round-trip fidelity for the backend schema fields (on
big_query_lineage.jsonthe restored assets equal the input exactly;resource_keyis only present when the input had a value) - Fully self-describing: All lookup sections are embedded in the document
- Human-readable: Plain text, no binary encoding
- CLI included: Easy command-line conversion tools
# Create virtual environment and install dev dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Run tests
pytest
# Run tests with coverage
pytest --cov=ldfSee LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.