Current section
Files
Jump to
Current section
Files
src/yog_io@json.erl
-module(yog_io@json).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/yog_io/json.gleam").
-export([default_export_options/0, export_options_with/2, to_json/2, to_json_file/3, to_d3_json/3, to_cytoscape_json/3, to_visjs_json/3, write/2, write_with/3, error_to_string/1, to_json_multi/2, to_json_file_multi/3]).
-export_type([json_format/0, json_export_options/2, json_error/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 (WRITE-ONLY).\n"
"\n"
" This module provides comprehensive JSON export capabilities for graph data,\n"
" supporting multiple formats used by popular visualization libraries.\n"
"\n"
" **Note:** This module currently supports WRITE operations only. Import/read\n"
" functionality is not implemented. For bidirectional I/O, consider using\n"
" GraphML, GDF, TGF, LEDA, or Pajek formats.\n"
"\n"
" ## Features\n"
"\n"
" - **Generic types** for nodes and edges (not just Strings)\n"
" - **Multiple JSON formats** (D3.js, Cytoscape.js, vis.js, etc.)\n"
" - **File write operations** (export JSON files directly)\n"
" - **Rich metadata** support (graph properties, rendering hints)\n"
" - **Format validation** and error reporting\n"
"\n"
" ## Quick Start\n"
"\n"
" ```gleam\n"
" import yog_io/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"
" // Export to file (simple)\n"
" let assert Ok(_) = json.write(\"graph.json\", graph)\n"
"\n"
" // Or with custom options\n"
" let assert Ok(_) = json.to_json_file(\n"
" graph,\n"
" \"graph.json\",\n"
" json.default_export_options(),\n"
" )\n"
" }\n"
" ```\n"
"\n"
" ## Format Support\n"
"\n"
" - **Generic**: Full metadata with type preservation\n"
" - **D3Force**: D3.js force-directed graphs\n"
" - **Cytoscape**: Cytoscape.js network visualization\n"
" - **VisJs**: vis.js network format\n"
" - **NetworkX**: Python NetworkX compatibility\n"
"\n"
" ## References\n"
"\n"
" - [JSON Specification](https://www.json.org/)\n"
" - [D3.js](https://d3js.org/)\n"
" - [Cytoscape.js](https://js.cytoscape.org/)\n"
" - [vis.js](https://visjs.github.io/vis-network/)\n"
).
-type json_format() :: generic | d3_force | cytoscape | vis_js | network_x.
-type json_export_options(AFXK, AFXL) :: {json_export_options,
json_format(),
boolean(),
gleam@option:option(fun((AFXK) -> gleam@json:json())),
gleam@option:option(fun((AFXL) -> gleam@json:json())),
boolean(),
gleam@option:option(gleam@dict:dict(binary(), gleam@json:json()))}.
-type json_error() :: {file_not_found, binary()} |
{invalid_json, binary()} |
{write_error, binary(), binary()} |
{read_error, binary(), binary()} |
{unsupported_format, binary()}.
-file("src/yog_io/json.gleam", 132).
?DOC(
" Creates default export options for String node and edge data.\n"
"\n"
" ## Default Settings\n"
"\n"
" - Format: Generic\n"
" - Include metadata: True\n"
" - Pretty print: True\n"
" - Node serializer: Converts strings to JSON strings\n"
" - Edge serializer: Converts strings to JSON strings\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let options = json.default_export_options()\n"
" json.to_json(graph, options)\n"
" ```\n"
).
-spec default_export_options() -> json_export_options(binary(), binary()).
default_export_options() ->
{json_export_options,
generic,
true,
{some, fun gleam@json:string/1},
{some, fun gleam@json:string/1},
true,
none}.
-file("src/yog_io/json.gleam", 165).
?DOC(
" Creates export options with custom serializers for generic types.\n"
"\n"
" Use this when your graph contains custom data types that need\n"
" special conversion to JSON.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" pub type Person {\n"
" Person(name: String, age: Int)\n"
" }\n"
"\n"
" let options = json.export_options_with(\n"
" node_serializer: fn(person) {\n"
" json.object([\n"
" #(\"name\", json.string(person.name)),\n"
" #(\"age\", json.int(person.age)),\n"
" ])\n"
" },\n"
" edge_serializer: fn(weight) { json.int(weight) },\n"
" )\n"
" ```\n"
).
-spec export_options_with(
fun((AFXO) -> gleam@json:json()),
fun((AFXP) -> gleam@json:json())
) -> json_export_options(AFXO, AFXP).
export_options_with(Node_serializer, Edge_serializer) ->
{json_export_options,
generic,
true,
{some, Node_serializer},
{some, Edge_serializer},
true,
none}.
-file("src/yog_io/json.gleam", 464).
-spec serialize_nodes_generic(
yog@model:graph(AGAI, AGAJ),
json_export_options(AGAI, AGAJ)
) -> list(gleam@json:json()).
serialize_nodes_generic(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_json = case erlang:element(4, Options) of
{some, Serializer} ->
Serializer(Data);
none ->
gleam@json:null()
end,
Node_obj = gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Id)},
{<<"data"/utf8>>, Data_json}]
),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 480).
-spec serialize_nodes_d3(
yog@model:graph(AGAP, AGAQ),
json_export_options(AGAP, AGAQ)
) -> list(gleam@json:json()).
serialize_nodes_d3(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Id_str = erlang:integer_to_binary(Id),
Base = [{<<"id"/utf8>>, gleam@json:string(Id_str)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 501).
-spec serialize_nodes_cytoscape(
yog@model:graph(AGAW, AGAX),
json_export_options(AGAW, AGAX)
) -> list(gleam@json:json()).
serialize_nodes_cytoscape(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(erlang:integer_to_binary(Id))}],
Data_obj = case erlang:element(4, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"label"/utf8>>, Serializer(Data)} | Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Node_obj = gleam@json:object([{<<"data"/utf8>>, Data_obj}]),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 521).
-spec serialize_nodes_visjs(
yog@model:graph(AGBD, AGBE),
json_export_options(AGBD, AGBE)
) -> list(gleam@json:json()).
serialize_nodes_visjs(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_label = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_label) | Acc]
end
).
-file("src/yog_io/json.gleam", 537).
-spec serialize_nodes_networkx(
yog@model:graph(AGBK, AGBL),
json_export_options(AGBK, AGBL)
) -> list(gleam@json:json()).
serialize_nodes_networkx(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 555).
-spec serialize_edges_generic(
yog@model:graph(AGBR, AGBS),
json_export_options(AGBR, AGBS)
) -> list(gleam@json:json()).
serialize_edges_generic(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Data_json = case erlang:element(5, Options) of
{some, Serializer} ->
Serializer(Edge_data);
none ->
gleam@json:null()
end,
Edge_obj = gleam@json:object(
[{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)},
{<<"data"/utf8>>, Data_json}]
),
[Edge_obj | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 585).
-spec serialize_edges_d3(
yog@model:graph(AGBY, AGBZ),
json_export_options(AGBY, AGBZ)
) -> list(gleam@json:json()).
serialize_edges_d3(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Base = [{<<"source"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(From_id)
)},
{<<"target"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(To_id)
)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"value"/utf8>>, Serializer(Edge_data)} |
Base];
none ->
Base
end,
[gleam@json:object(With_data) | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 397).
-spec to_d3_force_format(
yog@model:graph(AFZK, AFZL),
json_export_options(AFZK, AFZL)
) -> gleam@json:json().
to_d3_force_format(Graph, Options) ->
Nodes_json = serialize_nodes_d3(Graph, Options),
Edges_json = serialize_edges_d3(Graph, Options),
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 613).
-spec serialize_edges_cytoscape(
yog@model:graph(AGCF, AGCG),
json_export_options(AGCF, AGCG)
) -> list(gleam@json:json()).
serialize_edges_cytoscape(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Edge_id = <<<<<<"e"/utf8,
(erlang:integer_to_binary(From_id))/binary>>/binary,
"_"/utf8>>/binary,
(erlang:integer_to_binary(To_id))/binary>>,
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(Edge_id)},
{<<"source"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(From_id)
)},
{<<"target"/utf8>>,
gleam@json:string(
erlang:integer_to_binary(To_id)
)}],
Data_obj = case erlang:element(5, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"weight"/utf8>>,
Serializer(Edge_data)} |
Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Edge_obj = gleam@json:object(
[{<<"data"/utf8>>, Data_obj}]
),
[Edge_obj | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 410).
-spec to_cytoscape_format(
yog@model:graph(AFZQ, AFZR),
json_export_options(AFZQ, AFZR)
) -> gleam@json:json().
to_cytoscape_format(Graph, Options) ->
Nodes_json = serialize_nodes_cytoscape(Graph, Options),
Edges_json = serialize_edges_cytoscape(Graph, Options),
gleam@json:object(
[{<<"elements"/utf8>>,
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
)}]
)}]
).
-file("src/yog_io/json.gleam", 649).
-spec serialize_edges_visjs(
yog@model:graph(AGCM, AGCN),
json_export_options(AGCM, AGCN)
) -> list(gleam@json:json()).
serialize_edges_visjs(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Base = [{<<"from"/utf8>>, gleam@json:int(From_id)},
{<<"to"/utf8>>, gleam@json:int(To_id)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Edge_data)} |
Base];
none ->
Base
end,
[gleam@json:object(With_data) | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 428).
-spec to_visjs_format(
yog@model:graph(AFZW, AFZX),
json_export_options(AFZW, AFZX)
) -> gleam@json:json().
to_visjs_format(Graph, Options) ->
Nodes_json = serialize_nodes_visjs(Graph, Options),
Edges_json = serialize_edges_visjs(Graph, Options),
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)}]
).
-file("src/yog_io/json.gleam", 677).
-spec serialize_edges_networkx(
yog@model:graph(AGCT, AGCU),
json_export_options(AGCT, AGCU)
) -> list(gleam@json:json()).
serialize_edges_networkx(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, From_id, Targets) ->
Inner_edges = gleam@dict:fold(
Targets,
[],
fun(Inner_acc, To_id, Edge_data) ->
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Inner_acc;
_ ->
Base = [{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"weight"/utf8>>, Serializer(Edge_data)} |
Base];
none ->
Base
end,
[gleam@json:object(With_data) | Inner_acc]
end
end
),
lists:append([Inner_edges, Acc])
end
).
-file("src/yog_io/json.gleam", 441).
-spec to_networkx_format(
yog@model:graph(AGAC, AGAD),
json_export_options(AGAC, AGAD)
) -> gleam@json:json().
to_networkx_format(Graph, Options) ->
Nodes_json = serialize_nodes_networkx(Graph, Options),
Edges_json = serialize_edges_networkx(Graph, Options),
Directed = case erlang:element(2, Graph) of
directed ->
true;
undirected ->
false
end,
gleam@json:object(
[{<<"directed"/utf8>>, gleam@json:bool(Directed)},
{<<"multigraph"/utf8>>, gleam@json:bool(false)},
{<<"graph"/utf8>>, gleam@json:object([])},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 738).
-spec count_edges(yog@model:graph(any(), any())) -> integer().
count_edges(Graph) ->
case case erlang:element(2, Graph) of
undirected ->
2;
directed ->
1
end of
0 -> 0;
Gleam@denominator -> gleam@dict:fold(
erlang:element(4, Graph),
0,
fun(Acc, _, Targets) -> Acc + maps:size(Targets) end
)
div Gleam@denominator
end.
-file("src/yog_io/json.gleam", 707).
-spec build_metadata(
yog@model:graph(AGDA, AGDB),
json_export_options(AGDA, AGDB)
) -> gleam@json:json().
build_metadata(Graph, Options) ->
Graph_type = case erlang:element(2, Graph) of
directed ->
<<"directed"/utf8>>;
undirected ->
<<"undirected"/utf8>>
end,
Base_metadata = [{<<"graph_type"/utf8>>, gleam@json:string(Graph_type)},
{<<"node_count"/utf8>>,
gleam@json:int(maps:size(erlang:element(3, Graph)))},
{<<"edge_count"/utf8>>, gleam@json:int(count_edges(Graph))}],
With_custom = case erlang:element(7, Options) of
{some, Custom_metadata} ->
Custom_list = begin
_pipe = maps:to_list(Custom_metadata),
gleam@list:map(
_pipe,
fun(Pair) ->
{Key, Value} = Pair,
{Key, Value}
end
)
end,
lists:append(Base_metadata, Custom_list);
none ->
Base_metadata
end,
gleam@json:object(With_custom).
-file("src/yog_io/json.gleam", 372).
-spec to_generic_format(
yog@model:graph(AFZE, AFZF),
json_export_options(AFZE, AFZF)
) -> gleam@json:json().
to_generic_format(Graph, Options) ->
Nodes_json = serialize_nodes_generic(Graph, Options),
Edges_json = serialize_edges_generic(Graph, Options),
Base_object = [{<<"format"/utf8>>,
gleam@json:string(<<"yog-generic"/utf8>>)},
{<<"version"/utf8>>, gleam@json:string(<<"2.0"/utf8>>)},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}],
With_metadata = case erlang:element(3, Options) of
true ->
Metadata = build_metadata(Graph, Options),
[{<<"metadata"/utf8>>, Metadata} | Base_object];
false ->
Base_object
end,
gleam@json:object(With_metadata).
-file("src/yog_io/json.gleam", 203).
?DOC(
" Converts a graph to a JSON string.\n"
"\n"
" This function serializes a graph to JSON format according to the\n"
" specified options and format preset.\n"
"\n"
" **Time Complexity:** O(V + E)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/model\n"
" import yog_io/json\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 = json.to_json(graph, json.default_export_options())\n"
" // Returns JSON string representation\n"
" }\n"
" ```\n"
).
-spec to_json(yog@model:graph(AFXS, AFXT), json_export_options(AFXS, AFXT)) -> binary().
to_json(Graph, Options) ->
Json_obj = case erlang:element(2, Options) of
generic ->
to_generic_format(Graph, Options);
d3_force ->
to_d3_force_format(Graph, Options);
cytoscape ->
to_cytoscape_format(Graph, Options);
vis_js ->
to_visjs_format(Graph, Options);
network_x ->
to_networkx_format(Graph, Options)
end,
case erlang:element(6, Options) of
true ->
gleam@json:to_string(Json_obj);
false ->
gleam@json:to_string(Json_obj)
end.
-file("src/yog_io/json.gleam", 234).
?DOC(
" Exports a graph to a JSON file.\n"
"\n"
" This function writes the graph to a file in the specified JSON format.\n"
" The file will be created if it doesn't exist, or overwritten if it does.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case json.to_json_file(graph, \"output.json\", json.default_export_options()) {\n"
" Ok(_) -> io.println(\"Graph saved successfully\")\n"
" Error(json.WriteError(path, error)) -> {\n"
" io.println(\"Failed to write to \" <> path <> \": \" <> error)\n"
" }\n"
" Error(_) -> io.println(\"Unknown error\")\n"
" }\n"
" ```\n"
).
-spec to_json_file(
yog@model:graph(AFXY, AFXZ),
binary(),
json_export_options(AFXY, AFXZ)
) -> {ok, nil} | {error, json_error()}.
to_json_file(Graph, Path, Options) ->
Json_string = to_json(Graph, Options),
_pipe = simplifile:write(Path, Json_string),
gleam@result:map_error(
_pipe,
fun(Error) -> {write_error, Path, gleam@string:inspect(Error)} end
).
-file("src/yog_io/json.gleam", 255).
?DOC(
" Quick export for D3.js force-directed graphs with default settings.\n"
"\n"
" This is a convenience function that exports graphs in D3.js format\n"
" with sensible defaults.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let d3_json = json.to_d3_json(graph, json.string, json.string)\n"
" ```\n"
).
-spec to_d3_json(
yog@model:graph(AFYG, AFYH),
fun((AFYG) -> gleam@json:json()),
fun((AFYH) -> gleam@json:json())
) -> binary().
to_d3_json(Graph, Node_serializer, Edge_serializer) ->
Options = {json_export_options,
d3_force,
false,
{some, Node_serializer},
{some, Edge_serializer},
true,
none},
to_json(Graph, Options).
-file("src/yog_io/json.gleam", 273).
?DOC(" Quick export for Cytoscape.js with default settings.\n").
-spec to_cytoscape_json(
yog@model:graph(AFYK, AFYL),
fun((AFYK) -> gleam@json:json()),
fun((AFYL) -> gleam@json:json())
) -> binary().
to_cytoscape_json(Graph, Node_serializer, Edge_serializer) ->
Options = {json_export_options,
cytoscape,
false,
{some, Node_serializer},
{some, Edge_serializer},
true,
none},
to_json(Graph, Options).
-file("src/yog_io/json.gleam", 291).
?DOC(" Quick export for vis.js networks with default settings.\n").
-spec to_visjs_json(
yog@model:graph(AFYO, AFYP),
fun((AFYO) -> gleam@json:json()),
fun((AFYP) -> gleam@json:json())
) -> binary().
to_visjs_json(Graph, Node_serializer, Edge_serializer) ->
Options = {json_export_options,
vis_js,
false,
{some, Node_serializer},
{some, Edge_serializer},
true,
none},
to_json(Graph, Options).
-file("src/yog_io/json.gleam", 328).
?DOC(
" Writes a graph to a JSON file using default export options.\n"
"\n"
" This is a convenience function for String-based graphs that uses default\n"
" JSON export options. For more control over the output format, use\n"
" `to_json_file()` or `write_with()`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/model\n"
" import yog_io/json\n"
"\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 assert Ok(Nil) = json.write(\"graph.json\", graph)\n"
" ```\n"
).
-spec write(binary(), yog@model:graph(binary(), binary())) -> {ok, nil} |
{error, json_error()}.
write(Path, Graph) ->
to_json_file(Graph, Path, default_export_options()).
-file("src/yog_io/json.gleam", 362).
?DOC(
" Writes a graph to a JSON file with custom export options.\n"
"\n"
" This function provides full control over the JSON export format and\n"
" serialization behavior.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import gleam/json as gleam_json\n"
" import yog_io/json\n"
"\n"
" pub type Person {\n"
" Person(name: String, age: Int)\n"
" }\n"
"\n"
" let options = json.export_options_with(\n"
" node_serializer: fn(person) {\n"
" gleam_json.object([\n"
" #(\"name\", gleam_json.string(person.name)),\n"
" #(\"age\", gleam_json.int(person.age)),\n"
" ])\n"
" },\n"
" edge_serializer: fn(weight) { gleam_json.int(weight) },\n"
" )\n"
"\n"
" let assert Ok(Nil) = json.write_with(\"graph.json\", options, graph)\n"
" ```\n"
).
-spec write_with(
binary(),
json_export_options(AFYW, AFYX),
yog@model:graph(AFYW, AFYX)
) -> {ok, nil} | {error, json_error()}.
write_with(Path, Options, Graph) ->
to_json_file(Graph, Path, Options).
-file("src/yog_io/json.gleam", 758).
?DOC(
" Converts a JsonError to a human-readable string.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case json.to_json_file(graph, \"output.json\", options) {\n"
" Ok(_) -> \"Success!\"\n"
" Error(e) -> json.error_to_string(e)\n"
" }\n"
" ```\n"
).
-spec error_to_string(json_error()) -> binary().
error_to_string(Error) ->
case Error of
{file_not_found, Path} ->
<<"File not found: "/utf8, Path/binary>>;
{invalid_json, Msg} ->
<<"Invalid JSON: "/utf8, Msg/binary>>;
{write_error, Path@1, Msg@1} ->
<<<<<<"Write error at "/utf8, Path@1/binary>>/binary, ": "/utf8>>/binary,
Msg@1/binary>>;
{read_error, Path@2, Msg@2} ->
<<<<<<"Read error at "/utf8, Path@2/binary>>/binary, ": "/utf8>>/binary,
Msg@2/binary>>;
{unsupported_format, Fmt} ->
<<"Unsupported format: "/utf8, Fmt/binary>>
end.
-file("src/yog_io/json.gleam", 936).
-spec serialize_nodes_generic_multi(
yog@multi@model:multi_graph(AGFC, AGFD),
json_export_options(AGFC, AGFD)
) -> list(gleam@json:json()).
serialize_nodes_generic_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_json = case erlang:element(4, Options) of
{some, Serializer} ->
Serializer(Data);
none ->
gleam@json:null()
end,
Node_obj = gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Id)},
{<<"data"/utf8>>, Data_json}]
),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 952).
-spec serialize_nodes_d3_multi(
yog@multi@model:multi_graph(AGFJ, AGFK),
json_export_options(AGFJ, AGFK)
) -> list(gleam@json:json()).
serialize_nodes_d3_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Id_str = erlang:integer_to_binary(Id),
Base = [{<<"id"/utf8>>, gleam@json:string(Id_str)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 972).
-spec serialize_nodes_cytoscape_multi(
yog@multi@model:multi_graph(AGFQ, AGFR),
json_export_options(AGFQ, AGFR)
) -> list(gleam@json:json()).
serialize_nodes_cytoscape_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(erlang:integer_to_binary(Id))}],
Data_obj = case erlang:element(4, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"label"/utf8>>, Serializer(Data)} | Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Node_obj = gleam@json:object([{<<"data"/utf8>>, Data_obj}]),
[Node_obj | Acc]
end
).
-file("src/yog_io/json.gleam", 992).
-spec serialize_nodes_visjs_multi(
yog@multi@model:multi_graph(AGFX, AGFY),
json_export_options(AGFX, AGFY)
) -> list(gleam@json:json()).
serialize_nodes_visjs_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_label = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_label) | Acc]
end
).
-file("src/yog_io/json.gleam", 1008).
-spec serialize_nodes_networkx_multi(
yog@multi@model:multi_graph(AGGE, AGGF),
json_export_options(AGGE, AGGF)
) -> list(gleam@json:json()).
serialize_nodes_networkx_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(3, Graph),
[],
fun(Acc, Id, Data) ->
Base = [{<<"id"/utf8>>, gleam@json:int(Id)}],
With_data = case erlang:element(4, Options) of
{some, Serializer} ->
[{<<"data"/utf8>>, Serializer(Data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
).
-file("src/yog_io/json.gleam", 1026).
-spec serialize_edges_generic_multi(
yog@multi@model:multi_graph(AGGL, AGGM),
json_export_options(AGGL, AGGM)
) -> list(gleam@json:json()).
serialize_edges_generic_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Data_json = case erlang:element(5, Options) of
{some, Serializer} ->
Serializer(Edge_data);
none ->
gleam@json:null()
end,
Edge_obj = gleam@json:object(
[{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)},
{<<"data"/utf8>>, Data_json}]
),
[Edge_obj | Acc]
end
end
).
-file("src/yog_io/json.gleam", 1056).
-spec serialize_edges_d3_multi(
yog@multi@model:multi_graph(AGGS, AGGT),
json_export_options(AGGS, AGGT)
) -> list(gleam@json:json()).
serialize_edges_d3_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
From_str = erlang:integer_to_binary(From_id),
To_str = erlang:integer_to_binary(To_id),
Base = [{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"source"/utf8>>, gleam@json:string(From_str)},
{<<"target"/utf8>>, gleam@json:string(To_str)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"value"/utf8>>, Serializer(Edge_data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
end
).
-file("src/yog_io/json.gleam", 869).
-spec to_d3_force_format_multi(
yog@multi@model:multi_graph(AGEE, AGEF),
json_export_options(AGEE, AGEF)
) -> gleam@json:json().
to_d3_force_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_d3_multi(Graph, Options),
Edges_json = serialize_edges_d3_multi(Graph, Options),
gleam@json:object(
[{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 1086).
-spec serialize_edges_cytoscape_multi(
yog@multi@model:multi_graph(AGGZ, AGHA),
json_export_options(AGGZ, AGHA)
) -> list(gleam@json:json()).
serialize_edges_cytoscape_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Edge_id_str = <<"e"/utf8,
(erlang:integer_to_binary(Edge_id))/binary>>,
Source_str = erlang:integer_to_binary(From_id),
Target_str = erlang:integer_to_binary(To_id),
Data_fields = [{<<"id"/utf8>>,
gleam@json:string(Edge_id_str)},
{<<"source"/utf8>>, gleam@json:string(Source_str)},
{<<"target"/utf8>>, gleam@json:string(Target_str)}],
Data_obj = case erlang:element(5, Options) of
{some, Serializer} ->
gleam@json:object(
[{<<"label"/utf8>>, Serializer(Edge_data)} |
Data_fields]
);
none ->
gleam@json:object(Data_fields)
end,
Edge_obj = gleam@json:object([{<<"data"/utf8>>, Data_obj}]),
[Edge_obj | Acc]
end
end
).
-file("src/yog_io/json.gleam", 882).
-spec to_cytoscape_format_multi(
yog@multi@model:multi_graph(AGEK, AGEL),
json_export_options(AGEK, AGEL)
) -> gleam@json:json().
to_cytoscape_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_cytoscape_multi(Graph, Options),
Edges_json = serialize_edges_cytoscape_multi(Graph, Options),
gleam@json:object(
[{<<"elements"/utf8>>,
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
)}]
)}]
).
-file("src/yog_io/json.gleam", 1121).
-spec serialize_edges_visjs_multi(
yog@multi@model:multi_graph(AGHG, AGHH),
json_export_options(AGHG, AGHH)
) -> list(gleam@json:json()).
serialize_edges_visjs_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Base = [{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"from"/utf8>>, gleam@json:int(From_id)},
{<<"to"/utf8>>, gleam@json:int(To_id)}],
With_label = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"label"/utf8>>, Serializer(Edge_data)} | Base];
none ->
Base
end,
[gleam@json:object(With_label) | Acc]
end
end
).
-file("src/yog_io/json.gleam", 900).
-spec to_visjs_format_multi(
yog@multi@model:multi_graph(AGEQ, AGER),
json_export_options(AGEQ, AGER)
) -> gleam@json:json().
to_visjs_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_visjs_multi(Graph, Options),
Edges_json = serialize_edges_visjs_multi(Graph, Options),
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)}]
).
-file("src/yog_io/json.gleam", 1148).
-spec serialize_edges_networkx_multi(
yog@multi@model:multi_graph(AGHN, AGHO),
json_export_options(AGHN, AGHO)
) -> list(gleam@json:json()).
serialize_edges_networkx_multi(Graph, Options) ->
gleam@dict:fold(
erlang:element(4, Graph),
[],
fun(Acc, Edge_id, Edge_tuple) ->
{From_id, To_id, Edge_data} = Edge_tuple,
case erlang:element(2, Graph) of
undirected when From_id > To_id ->
Acc;
_ ->
Base = [{<<"id"/utf8>>, gleam@json:int(Edge_id)},
{<<"source"/utf8>>, gleam@json:int(From_id)},
{<<"target"/utf8>>, gleam@json:int(To_id)}],
With_data = case erlang:element(5, Options) of
{some, Serializer} ->
[{<<"weight"/utf8>>, Serializer(Edge_data)} | Base];
none ->
Base
end,
[gleam@json:object(With_data) | Acc]
end
end
).
-file("src/yog_io/json.gleam", 913).
-spec to_networkx_format_multi(
yog@multi@model:multi_graph(AGEW, AGEX),
json_export_options(AGEW, AGEX)
) -> gleam@json:json().
to_networkx_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_networkx_multi(Graph, Options),
Edges_json = serialize_edges_networkx_multi(Graph, Options),
Directed = case erlang:element(2, Graph) of
directed ->
true;
undirected ->
false
end,
gleam@json:object(
[{<<"directed"/utf8>>, gleam@json:bool(Directed)},
{<<"multigraph"/utf8>>, gleam@json:bool(true)},
{<<"graph"/utf8>>, gleam@json:object([])},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"links"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}]
).
-file("src/yog_io/json.gleam", 1177).
-spec build_metadata_multi(
yog@multi@model:multi_graph(AGHU, AGHV),
json_export_options(AGHU, AGHV)
) -> gleam@json:json().
build_metadata_multi(Graph, Options) ->
Graph_type = case erlang:element(2, Graph) of
directed ->
<<"directed"/utf8>>;
undirected ->
<<"undirected"/utf8>>
end,
Base_metadata = [{<<"graph_type"/utf8>>, gleam@json:string(Graph_type)},
{<<"multigraph"/utf8>>, gleam@json:bool(true)},
{<<"node_count"/utf8>>,
gleam@json:int(maps:size(erlang:element(3, Graph)))},
{<<"edge_count"/utf8>>,
gleam@json:int(maps:size(erlang:element(4, Graph)))}],
With_custom = case erlang:element(7, Options) of
{some, Custom_metadata} ->
Custom_list = begin
_pipe = maps:to_list(Custom_metadata),
gleam@list:map(
_pipe,
fun(Pair) ->
{Key, Value} = Pair,
{Key, Value}
end
)
end,
lists:append(Custom_list, Base_metadata);
none ->
Base_metadata
end,
gleam@json:object(With_custom).
-file("src/yog_io/json.gleam", 844).
-spec to_generic_format_multi(
yog@multi@model:multi_graph(AGDY, AGDZ),
json_export_options(AGDY, AGDZ)
) -> gleam@json:json().
to_generic_format_multi(Graph, Options) ->
Nodes_json = serialize_nodes_generic_multi(Graph, Options),
Edges_json = serialize_edges_generic_multi(Graph, Options),
Base_object = [{<<"format"/utf8>>,
gleam@json:string(<<"yog-generic"/utf8>>)},
{<<"version"/utf8>>, gleam@json:string(<<"2.0"/utf8>>)},
{<<"nodes"/utf8>>,
gleam@json:array(Nodes_json, fun gleam@function:identity/1)},
{<<"edges"/utf8>>,
gleam@json:array(Edges_json, fun gleam@function:identity/1)}],
With_metadata = case erlang:element(3, Options) of
true ->
Metadata = build_metadata_multi(Graph, Options),
[{<<"metadata"/utf8>>, Metadata} | Base_object];
false ->
Base_object
end,
gleam@json:object(With_metadata).
-file("src/yog_io/json.gleam", 797).
?DOC(
" Converts a multigraph to a JSON string.\n"
"\n"
" This function serializes a multigraph to JSON format with edge IDs\n"
" to preserve parallel edges. All formats include unique edge identifiers.\n"
"\n"
" **Time Complexity:** O(V + E)\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import yog/multi/model as multi\n"
" import yog_io/json\n"
"\n"
" pub fn main() {\n"
" let graph =\n"
" multi.new(multi.Directed)\n"
" |> multi.add_node(1, \"Alice\")\n"
" |> multi.add_node(2, \"Bob\")\n"
" |> multi.add_edge(from: 1, to: 2, with: \"follows\")\n"
" |> multi.add_edge(from: 1, to: 2, with: \"mentions\")\n"
"\n"
" let json_string = json.to_json_multi(graph, json.default_export_options())\n"
" // Returns JSON with edge IDs for parallel edges\n"
" }\n"
" ```\n"
).
-spec to_json_multi(
yog@multi@model:multi_graph(AGDK, AGDL),
json_export_options(AGDK, AGDL)
) -> binary().
to_json_multi(Graph, Options) ->
Json_obj = case erlang:element(2, Options) of
generic ->
to_generic_format_multi(Graph, Options);
d3_force ->
to_d3_force_format_multi(Graph, Options);
cytoscape ->
to_cytoscape_format_multi(Graph, Options);
vis_js ->
to_visjs_format_multi(Graph, Options);
network_x ->
to_networkx_format_multi(Graph, Options)
end,
case erlang:element(6, Options) of
true ->
gleam@json:to_string(Json_obj);
false ->
gleam@json:to_string(Json_obj)
end.
-file("src/yog_io/json.gleam", 831).
?DOC(
" Exports a multigraph to a JSON file.\n"
"\n"
" This function writes the multigraph to a file in the specified JSON format.\n"
" The file will be created if it doesn't exist, or overwritten if it does.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" case json.to_json_file_multi(graph, \"output.json\", json.default_export_options()) {\n"
" Ok(_) -> io.println(\"Multigraph saved successfully\")\n"
" Error(json.WriteError(path, error)) -> {\n"
" io.println(\"Failed to write to \" <> path <> \": \" <> error)\n"
" }\n"
" Error(_) -> io.println(\"Unknown error\")\n"
" }\n"
" ```\n"
).
-spec to_json_file_multi(
yog@multi@model:multi_graph(AGDQ, AGDR),
binary(),
json_export_options(AGDQ, AGDR)
) -> {ok, nil} | {error, json_error()}.
to_json_file_multi(Graph, Path, Options) ->
Json_string = to_json_multi(Graph, Options),
_pipe = simplifile:write(Path, Json_string),
gleam@result:map_error(
_pipe,
fun(Error) -> {write_error, Path, gleam@string:inspect(Error)} end
).