1% -*- mode: slang; mode: fold -*- 2 3import("json"); 4 5%{{{ Type Handlers for json_encode 6 7private variable Type_Map = Ref_Type[0]; 8 9private define add_type_handler () 10{ 11 variable func = (); 12 loop (_NARGS-1) 13 { 14 variable type = (); 15 variable idx = __class_id (type); 16 variable n = length (Type_Map); 17 if (idx >= n) 18 { 19 variable new_map = Ref_Type[idx+1]; 20 new_map[[0:n-1]] = Type_Map; 21 Type_Map = new_map; 22 } 23 Type_Map[idx] = func; 24 } 25} 26 27private define get_encode_func (type) 28{ 29 try 30 { 31 variable func = Type_Map[__class_id (type)]; 32 if (func == NULL) throw IndexError; 33 return func; 34 } 35 catch IndexError: 36 throw Json_Invalid_Json_Error, "$type does not represent a JSON data structure"$; 37} 38%}}} 39 40private define json_encode_string (indent, q, data) %{{{ 41{ 42 return _json_encode_string (data); 43} 44add_type_handler (String_Type, BString_Type, &json_encode_string); 45%}}} 46 47private define json_encode_boolean (indent, q, data) %{{{ 48{ 49 if (data == 1) return "true"B; 50 if (data == 0) return "false"B; 51 throw Json_Invalid_Json_Error, sprintf (`invalid boolean value '\%03o'; only '\000' and '\001' are allowed`, data); 52} 53add_type_handler (UChar_Type, &json_encode_boolean); 54%}}} 55 56private define json_encode_null (indent, q, data) %{{{ 57{ 58 return "null"B; 59} 60add_type_handler (Null_Type, &json_encode_null); 61%}}} 62 63private define json_encode_number (indent, q, data) %{{{ 64{ 65 return string (data); 66} 67add_type_handler ( 68 Char_Type, % UChar_Type, 69 Short_Type, UShort_Type, 70 Int_Type, UInt_Type, 71 Long_Type, ULong_Type, 72#ifexists LLong_Type 73 LLong_Type, ULLong_Type, 74#endif 75 Float_Type, Double_Type, 76 &json_encode_number); 77%}}} 78 79private define json_encode_object (indent, q, object) %{{{ 80{ 81 variable json = "{"B; 82 variable keys = get_struct_field_names (object); 83 variable n_values = length (keys); 84 if (n_values) 85 { 86 % pvs indent KEY nsep VAL vsep pvs indent KEY nsep VAL vsep 87 % ... pvs indent KEY nsep VAL pvs 88 89 variable new_indent = bstrcat (indent, q.indent); 90 variable sep = bstrcat (q.vsep, q.post_vsep, new_indent); 91 variable nsep = q.nsep; 92 93 variable key = keys[0]; 94 variable val = get_struct_field(object, key); 95 variable type = typeof (val); 96 variable func = get_encode_func (type); 97 json = bstrcat (__tmp(json), q.post_vsep, new_indent, 98 _json_encode_string (key), nsep, (@func)(new_indent, q, val)); 99 100 variable i; 101 _for i (1, n_values-1) 102 { 103 key = keys[i]; 104 val = get_struct_field(object, key); 105 variable next_type = typeof (val); 106 if (next_type == String_Type) 107 { 108 json = bstrcat (__tmp(json), sep, _json_encode_string (key), 109 nsep, _json_encode_string (val)); 110 continue; 111 } 112 113 if (next_type != type) 114 (type, func) = (next_type, get_encode_func (next_type)); 115 json = bstrcat (__tmp(json), sep, _json_encode_string (key), 116 nsep, (@func)(new_indent, q, val)); 117 } 118 json = bstrcat (__tmp(json), q.post_vsep); 119 } 120 return bstrcat (__tmp(json), indent, "}"); 121} 122add_type_handler (Struct_Type, &json_encode_object); 123%}}} 124 125private define json_encode_array (indent, q, array) %{{{ 126{ 127 variable json = "["B; 128 variable n_values = length (array); 129 if (n_values) 130 { 131 % pvs, new_indent, VALUE, vsep, pvs, new_indent, VALUE, vsep, pvs, ..., new_indent, VALUE, pvs 132 133 variable new_indent = bstrcat (indent, q.indent); 134 variable sep = bstrcat (q.vsep, q.post_vsep, new_indent); 135 136 variable i = 0; 137 variable a = array[i]; 138 variable type = typeof (a); 139 variable func = get_encode_func (type); 140 json = bstrcat (__tmp(json), q.post_vsep, 141 new_indent, (@func)(new_indent, q, a)); 142 143 if ((typeof (array) == Array_Type) && not any (_isnull (array))) 144 { 145 if (type == String_Type) 146 _for i (1, n_values-1) 147 json = bstrcat (__tmp(json), sep, _json_encode_string (array[i])); 148 else 149 _for i (1, n_values-1) 150 json = bstrcat (__tmp(json), sep, (@func)(new_indent, q, array[i])); 151 } 152 else _for i (1, n_values-1) 153 { 154 a = array[i]; 155 variable next_type = typeof (a); 156 if (next_type == String_Type) 157 { 158 json = bstrcat (__tmp(json), sep, _json_encode_string (a)); 159 continue; 160 } 161 162 if (next_type != type) 163 (type, func) = (next_type, get_encode_func (next_type)); 164 json = bstrcat (__tmp(json), sep, (@func)(new_indent, q, a)); 165 } 166 167 json = bstrcat (__tmp(json), q.post_vsep); 168 } 169 return bstrcat (__tmp(json), indent, "]"); 170} 171add_type_handler (List_Type, Array_Type, &json_encode_array); 172%}}} 173 174private define default_handler (indent, q, data) %{{{ 175{ 176 if (0 < __is_numeric (data) < 3) 177 return json_encode_number (data); 178 179 if (is_struct_type (data)) 180 return json_encode_object (data); 181 182 variable type = _typeof (data); 183 throw Json_Invalid_Json_Error, "$type does not represent a JSON data structure"$; 184} 185Type_Map[where (_isnull (Type_Map))] = &default_handler; 186%}}} 187 188% process_qualifiers %{{{ 189 190private define split_post_vsep (post_vsep) %{{{ 191{ 192 variable indent = ""; 193 variable parts = strchop (post_vsep, '\n', 0); 194 if (length (parts) > 1) 195 { 196 indent = parts[-1]; 197 parts[-1] = ""; 198 post_vsep = strjoin (parts, "\n"); 199 } 200 return (indent, post_vsep); 201} 202%}}} 203 204private define only_whitespace (s) 205{ 206 return ""B + str_delete_chars (s, "^ \t\n\r"); 207} 208 209private define process_qualifiers () 210{ 211 variable post_vsep = split_post_vsep (qualifier ("post_vsep", "")); 212 variable indent = (); % other return value from split_post_vsep 213 variable q = struct { 214 pre_nsep = only_whitespace (qualifier ("pre_nsep", "")), 215 post_nsep = only_whitespace (qualifier ("post_nsep", "")), 216 pre_vsep = only_whitespace (qualifier ("pre_vsep", "")), 217 post_vsep = only_whitespace (post_vsep), 218 indent = only_whitespace (indent), 219 }; 220 return struct { 221 nsep = q.pre_nsep + ":" + q.post_nsep, 222 vsep = q.pre_vsep + ",", 223 @q 224 }; 225} 226%}}} 227 228define json_encode (data) 229{ 230 variable q = process_qualifiers (;; __qualifiers); 231 variable func = get_encode_func (typeof (data)); 232 variable json = (@func)(""B, q, data); 233 return typecast (json, String_Type); 234} 235