Two pictures of the same graph
A JSON-LD document is two things at once, and they do not look alike. It is a tree, because it is JSON, and a developer will index into it with dots and brackets. It is also a graph, because that is what it denotes, and a machine will traverse it by following IRIs. Most of the difficulty of the format is that these two pictures come apart, and most of the tooling shows you only one.
Where the pictures diverge
They agree about the simple case. A node with three scalar properties is a three-child tree and a node with three outgoing edges, and the drawings are isomorphic. Nobody needs a tool for that.
They come apart in three places, and each place corresponds to a JSON-LD 1.1 feature people find hard.
Structure with no semantics
@nest puts a box in the JSON that does not exist in the graph. The
document has an object called details; the graph has no
details node and no details edge. The nesting is there so
a thirty-key node object can be read by a human.
@container is the same phenomenon in a different guise.
@set guarantees an array where there might have been a scalar;
@index turns an array into a keyed object and throws the keys away;
@language does the same and keeps them, as language tags on literals.
In every case the JSON changes shape. In most, the graph is unmoved.
@index is the sharpest version: the key is right there in the document,
clearly meaningful to whoever wrote it, and it is not in the graph at all. Two
readers of the same file disagree about whether that key is data.
Semantics with no structure
The reverse case is rarer and more disorienting. @reverse produces an
edge that points the opposite way from the nesting: the document reads as though the
outer node is the subject, and in the graph the inner node is. A developer walking
the tree and a machine walking the graph traverse the same relationship in opposite
directions.
Type coercion belongs here too. @type: @id is invisible in the document
— the value is a string either way — and it decides whether the graph has an edge or
a literal. There is no structural difference to see. There is only a difference in
what the structure means.
Position-dependent meaning
Scoped contexts break the assumption that a key means one thing in a document. Two
keys spelled name can be two properties, distinguished only by where
they sit. The tree picture handles this naturally — position is what a tree is made
of. The graph picture does not have position at all, so the same document produces
two distinct edges with no visible reason.
Neither picture is lying. They are answering different questions.
Why one picture is not enough
The standard tools each pick a side, and each side has a characteristic blind spot.
A JSON editor shows the tree. You see the shape you will write,
which is genuinely most of the day-to-day work. What you cannot see is whether
anything you wrote survived, whether that string became an edge or a literal, or
whether the name here is the same property as the name
twelve lines up.
A graph visualiser shows the graph. You see the model, which is the thing you are actually trying to design. What you cannot see is what a developer will have to type to produce it — and that is the deliverable. A beautifully normalised graph can correspond to a document so awkward that every consumer writes a wrapper around it.
An expanded-form dump shows the truth and is unreadable. It is the ground truth and it is where experienced people go when something is wrong, but it is a debugging output, not a design surface. You cannot hold a fifty-term vocabulary in your head in expanded form.
So: both, side by side, sharing one selection. Select a term and it lights up in the JSON pane and in the graph pane simultaneously. Change a facet and watch which pane moves. That is the whole design of the canvas in this tool, and the coordination is the feature — two unlinked panes would be two tabs.
Drawing absence
The interesting design problem is not showing the two pictures. It is showing the relationship between them, and specifically showing the places where a feature exists on one side and not the other.
The naive approach draws each pane independently and lets the user notice. That
fails, because absence is invisible: if @nest simply does not appear in
the graph pane, the user sees a graph pane that looks fine. Nothing signals that
something was there and is not.
The approach taken here is to draw the absence explicitly. A
@nest group is a solid box in the tree pane and a dashed, greyed
outline in the graph pane, labelled as structural. A @container: @set
changes the tree pane and puts a small no-effect marker on the corresponding graph
edge. Selecting a term with a facet that affects only one side tells you so, in
words, rather than leaving you to infer it from a pane that did not move.
This sounds like a small interaction detail. In use it is the difference between a tool that shows you two views and a tool that teaches you the format, because the gap between the pictures is the format's difficulty, and the gap is what gets drawn.
An aside on property graphs
People coming from Neo4j or TigerGraph ask, reasonably, why RDF makes this so hard when a property graph does not. The answer is that a property graph has a third picture the RDF model does not: edges and nodes carry their own key-value attributes, so "this relationship has a weight" is a first-class thing rather than a reification exercise.
That is genuinely more convenient for the modelling most people do, and it is why property graphs dominate inside single organisations. What it gives up is the merge-by-IRI property: two property graphs from different sources do not combine without a mapping, because their node identities and attribute names are local. RDF pays for global mergeability with an austere model, and JSON-LD's containers, nests and scopes are all attempts to make that austere model pleasant to write by hand. Which is exactly why the two pictures diverge — every one of those features is ergonomics layered over a model that does not have them.
Worth knowing if you are choosing. Also worth knowing if you have chosen RDF and are wondering why the format keeps feeling like it has two personalities. It does, and there is a reason.
What to do with this
Three practical habits, whether or not you use this tool.
When you design, draw the graph. Nodes, edges, types, cardinality. Decide what exists and what points at what before deciding what the JSON looks like. A model designed from the JSON side acquires accidental structure — nesting that exists because the API returned it that way — and accidental structure is very hard to remove once documents exist.
When you review, read the expansion. Not the context, not the example document: the expansion of the example document. It is the only artifact that shows what was actually said. Ten minutes reading expanded output will find things an hour reading the context will not, because the context shows intent and the expansion shows outcome.
When you ship, test both pictures. Test that the JSON shape is what consumers expect — that is a contract with developers. Test separately that the graph is what you meant — that is a contract with data. They break independently, and a test suite that only covers one of them will let the other rot quietly for a year.
The two pictures are not a flaw in JSON-LD. They are what it is for: a graph model that a person can write as a tree. But a format that is two things at once needs tools that admit it, and the ones that show you a single picture are, in the most literal sense, telling you half the story.