-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" " 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", 66). ?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", 121). ?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)}] ) ).