Current section

Files

Jump to
yog src yog@render@json.erl
Raw

src/yog@render@json.erl

-module(yog@render@json).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog/render/json.gleam").
-export([default_json_options/0, to_json/2]).
-export_type([json_options/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" JSON format export for graph data exchange.\n"
"\n"
" **⚠️ DEPRECATION NOTICE**\n"
"\n"
" This module is planned to be moved to `yog_io` in a future release.\n"
" The API will be redesigned to support:\n"
" - Generic types (not just String node/edge data)\n"
" - Multiple JSON formats (D3.js, Cytoscape.js, vis.js, etc.)\n"
" - File I/O operations (reading/writing JSON files)\n"
" - Enhanced metadata support\n"
"\n"
" For details on the planned API improvements, see `md_dumps/JSON_API_SPEC.md`.\n"
"\n"
" ---\n"
"\n"
" This module exports graphs to JSON format, suitable for:\n"
" - Interoperability with web applications\n"
" - Storage in document databases (MongoDB, CouchDB)\n"
" - Visualization libraries (D3.js, Cytoscape.js, vis.js)\n"
" - API responses and data exchange\n"
"\n"
" ## Output Format\n"
"\n"
" The default output format is:\n"
" ```json\n"
" {\n"
" \"nodes\": [\n"
" { \"id\": 1, \"label\": \"Node A\" },\n"
" { \"id\": 2, \"label\": \"Node B\" }\n"
" ],\n"
" \"edges\": [\n"
" { \"source\": 1, \"target\": 2, \"weight\": \"5\" }\n"
" ]\n"
" }\n"
" ```\n"
"\n"
" ## Customization\n"
"\n"
" Use `JsonOptions` with custom `node_mapper` and `edge_mapper` functions\n"
" to control the exact JSON structure. This allows integration with any\n"
" schema required by your frontend or external system.\n"
"\n"
" ## Example: D3.js Integration\n"
"\n"
" ```gleam\n"
" import yog/render/json\n"
"\n"
" // Export for D3.js force simulation\n"
" let json_string = json.to_string(graph)\n"
" // Use in JavaScript: d3.forceSimulation(JSON.parse(json_string).nodes)\n"
" ```\n"
"\n"
" ## References\n"
"\n"
" - [JSON Specification](https://www.json.org/)\n"
" - [D3.js Graph Visualization](https://d3js.org/d3-force)\n"
" - [Cytoscape.js](https://js.cytoscape.org/)\n"
).
-type json_options() :: {json_options,
fun((integer(), binary()) -> gleam@json:json()),
fun((integer(), integer(), binary()) -> gleam@json:json())}.
-file("src/yog/render/json.gleam", 79).
?DOC(
" Creates default JSON options.\n"
"\n"
" Nodes are `{ \"id\": 1, \"label\": \"Node A\" }`.\n"
" Edges are `{ \"source\": 1, \"target\": 2, \"weight\": \"5\" }`.\n"
).
-spec default_json_options() -> json_options().
default_json_options() ->
{json_options,
fun(Id, Data) ->
gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Id)},
{<<"label"/utf8>>, gleam@json:string(Data)}]
)
end,
fun(From, To, Weight) ->
gleam@json:object(
[{<<"source"/utf8>>, gleam@json:int(From)},
{<<"target"/utf8>>, gleam@json:int(To)},
{<<"weight"/utf8>>, gleam@json:string(Weight)}]
)
end}.
-file("src/yog/render/json.gleam", 134).
?DOC(
" Converts a graph to a JSON string compatible with many visualization libraries (e.g., D3.js).\n"
"\n"
" The graph's node data and edge data must be convertible to strings.\n"
"\n"
" **Time Complexity:** O(V + E)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import gleam/io\n"
" import gleam/json\n"
" import yog/model\n"
"\n"
" pub fn main() {\n"
" let graph =\n"
" model.new(model.Directed)\n"
" |> model.add_node(1, \"Alice\")\n"
" |> model.add_node(2, \"Bob\")\n"
" |> model.add_edge(from: 1, to: 2, with: \"follows\")\n"
"\n"
" let json_string = yog_json.to_json(graph, yog_json.default_json_options())\n"
" io.println(json_string)\n"
" }\n"
" ```\n"
"\n"
" This outputs:\n"
" ````json\n"
" {\n"
" \"nodes\": [\n"
" {\"id\": 1, \"label\": \"Alice\"},\n"
" {\"id\": 2, \"label\": \"Bob\"}\n"
" ],\n"
" \"edges\": [\n"
" {\"source\": 1, \"target\": 2, \"weight\": \"follows\"}\n"
" ]\n"
" }\n"
" ````\n"
).
-spec to_json(yog@model:graph(binary(), binary()), json_options()) -> binary().
to_json(Graph, Options) ->
Nodes_json = gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) -> [(erlang:element(2, Options))(Id, Data) | Acc] end
),
Edges_json = gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc@1, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Weight) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
[(erlang:element(3, Options))(
From_id,
To_id,
Weight
) |
Inner_acc]
end
end
),
lists:append([Inner_edges, Acc@1])
end
),
gleam@json:to_string(
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
)
).