1 /** 2 * JSON module 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-03-09 8 */ 9 10 module uniconf.json; 11 12 private 13 { 14 import std.json : JSONValue, JSONType, parseJSON, JSONException, toJSON; 15 16 import uniconf.core : UniConfException; 17 import uninode.node : isUniNode; 18 } 19 20 21 /** 22 * Conert Json to UniConf 23 */ 24 UniConf toUniConf(UniConf)(auto ref const JSONValue root) pure @safe 25 if (isUniNode!UniConf) 26 { 27 UniConf convert(ref const JSONValue node) pure @trusted 28 { 29 switch(node.type) 30 { 31 case JSONType.NULL: 32 return UniConf(); 33 case JSONType.TRUE: 34 return UniConf(true); 35 case JSONType.FALSE: 36 return UniConf(false); 37 case JSONType.INTEGER: 38 return UniConf(node.integer); 39 case JSONType.UINTEGER: 40 return UniConf(node.uinteger); 41 case JSONType.FLOAT: 42 return UniConf(node.floating); 43 case JSONType.STRING: 44 return UniConf(node.str); 45 case JSONType.ARRAY: 46 { 47 size_t len = node.array.length; 48 UniConf[] arr = new UniConf[len]; 49 foreach(size_t i, JSONValue ch; node.array) 50 arr[i] = convert(ch); 51 return UniConf(arr); 52 } 53 case JSONType.OBJECT: 54 { 55 UniConf[string] map; 56 foreach (string key, JSONValue ch; node.object) 57 map[key] = convert(ch); 58 return UniConf(map); 59 } 60 default: 61 return UniConf(); 62 } 63 } 64 65 return convert(root); 66 } 67 68 69 /** 70 * Convert UniConf to Json 71 */ 72 JSONValue toJson(UniConf)(auto ref const UniConf root) @safe 73 if (isUniNode!UniConf) 74 { 75 JSONValue convert(ref const UniConf node) @trusted 76 { 77 switch(node.tag) 78 { 79 case UniConf.Tag.nil: 80 return JSONValue(); 81 case UniConf.Tag.boolean: 82 return JSONValue(node.get!bool); 83 case UniConf.Tag.integer: 84 return JSONValue(node.get!long); 85 case UniConf.Tag.uinteger: 86 return JSONValue(node.get!ulong); 87 case UniConf.Tag.floating: 88 return JSONValue(node.get!double); 89 case UniConf.Tag.text: 90 return JSONValue(node.get!string); 91 case UniConf.Tag.sequence: 92 { 93 size_t len = node.length; 94 JSONValue[] arr = new JSONValue[len]; 95 foreach(size_t i, ref const UniConf ch; node) 96 arr[i] = convert(ch); 97 return JSONValue(arr); 98 } 99 case UniConf.Tag.mapping: 100 { 101 JSONValue[string] map; 102 foreach (string key, ref const UniConf ch; node) 103 map[key] = convert(ch); 104 return JSONValue(map); 105 } 106 default: 107 return JSONValue(); 108 } 109 } 110 111 return convert(root); 112 } 113 114 @("Should work convert json to config and back") 115 @safe unittest 116 { 117 import uninode.node : UniNode; 118 119 const val = JSONValue([JSONValue(1), JSONValue("hello"), JSONValue(["one": JSONValue(1)])]); 120 const conf = val.toUniConf!UniNode(); 121 const js = conf.toJson(); 122 assert (js == val); 123 } 124 125 126 /** 127 * Parse UniConf from string 128 */ 129 UniConf parseJson(UniConf)(string data) @safe 130 if (isUniNode!UniConf) 131 { 132 JSONValue root; 133 try 134 root = parseJSON(data); 135 catch (JSONException e) 136 throw new UniConfException("Error loading json from a string:", 137 e.file, e.line, e); 138 return toUniConf!UniConf(root); 139 } 140 141 142 /** 143 * Convert UniConf to json string 144 */ 145 string saveJson(UniConf)(auto ref const UniConf node, bool pretty=false) @safe 146 { 147 const json = toJson!UniConf(node); 148 return toJSON(json, pretty); 149 } 150 151 @("Should work parseJson and saveJson method") 152 @safe unittest 153 { 154 import uninode.tree : UniTree; 155 156 enum json = `{"client":{"host":"localhost","port":44}}`; 157 158 const conf = json.parseJson!UniTree; 159 assert (conf.get!int("client.port") == 44); 160 assert (conf.get!string("client.host") == "localhost"); 161 162 const js = saveJson(conf); 163 assert (js == json); 164 } 165