| 1 /* purple |
|
| 2 * |
|
| 3 * Purple is the legal property of its developers, whose names are too numerous |
|
| 4 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 5 * source distribution. |
|
| 6 * |
|
| 7 * This program is free software; you can redistribute it and/or modify |
|
| 8 * it under the terms of the GNU General Public License as published by |
|
| 9 * the Free Software Foundation; either version 2 of the License, or |
|
| 10 * (at your option) any later version. |
|
| 11 * |
|
| 12 * This program is distributed in the hope that it will be useful, |
|
| 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 15 * GNU General Public License for more details. |
|
| 16 * |
|
| 17 * You should have received a copy of the GNU General Public License |
|
| 18 * along with this program; if not, write to the Free Software |
|
| 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
|
| 20 */ |
|
| 21 |
|
| 22 #include <glib/gi18n-lib.h> |
|
| 23 |
|
| 24 #include <stdarg.h> |
|
| 25 #include <string.h> |
|
| 26 |
|
| 27 #include "json.h" |
|
| 28 #include "util.h" |
|
| 29 |
|
| 30 typedef struct _FbJsonValue FbJsonValue; |
|
| 31 |
|
| 32 struct _FbJsonValue |
|
| 33 { |
|
| 34 const gchar *expr; |
|
| 35 FbJsonType type; |
|
| 36 gboolean required; |
|
| 37 GValue value; |
|
| 38 }; |
|
| 39 |
|
| 40 /** |
|
| 41 * FbJsonValues: |
|
| 42 * |
|
| 43 * Represents a JSON value handler. |
|
| 44 */ |
|
| 45 struct _FbJsonValues |
|
| 46 { |
|
| 47 GObject parent; |
|
| 48 |
|
| 49 JsonNode *root; |
|
| 50 GQueue *queue; |
|
| 51 GList *next; |
|
| 52 |
|
| 53 gboolean isarray; |
|
| 54 JsonArray *array; |
|
| 55 guint index; |
|
| 56 |
|
| 57 GError *error; |
|
| 58 }; |
|
| 59 |
|
| 60 G_DEFINE_TYPE(FbJsonValues, fb_json_values, G_TYPE_OBJECT); |
|
| 61 |
|
| 62 static void |
|
| 63 fb_json_values_dispose(GObject *obj) |
|
| 64 { |
|
| 65 FbJsonValues *values = FB_JSON_VALUES(obj); |
|
| 66 |
|
| 67 if(values->queue != NULL) { |
|
| 68 while(!g_queue_is_empty(values->queue)) { |
|
| 69 FbJsonValue *value = g_queue_pop_head(values->queue); |
|
| 70 |
|
| 71 if(G_IS_VALUE(&value->value)) { |
|
| 72 g_value_unset(&value->value); |
|
| 73 } |
|
| 74 |
|
| 75 g_free(value); |
|
| 76 } |
|
| 77 } |
|
| 78 |
|
| 79 g_clear_pointer(&values->array, json_array_unref); |
|
| 80 g_clear_error(&values->error); |
|
| 81 g_clear_pointer(&values->queue, g_queue_free); |
|
| 82 } |
|
| 83 |
|
| 84 static void |
|
| 85 fb_json_values_class_init(FbJsonValuesClass *klass) |
|
| 86 { |
|
| 87 GObjectClass *gklass = G_OBJECT_CLASS(klass); |
|
| 88 |
|
| 89 gklass->dispose = fb_json_values_dispose; |
|
| 90 } |
|
| 91 |
|
| 92 static void |
|
| 93 fb_json_values_init(FbJsonValues *values) |
|
| 94 { |
|
| 95 values->queue = g_queue_new(); |
|
| 96 } |
|
| 97 |
|
| 98 GQuark |
|
| 99 fb_json_error_quark(void) |
|
| 100 { |
|
| 101 static GQuark q = 0; |
|
| 102 |
|
| 103 if (G_UNLIKELY(q == 0)) { |
|
| 104 q = g_quark_from_static_string("fb-json-error-quark"); |
|
| 105 } |
|
| 106 |
|
| 107 return q; |
|
| 108 } |
|
| 109 |
|
| 110 JsonBuilder * |
|
| 111 fb_json_bldr_new(JsonNodeType type) |
|
| 112 { |
|
| 113 JsonBuilder *bldr; |
|
| 114 |
|
| 115 bldr = json_builder_new(); |
|
| 116 |
|
| 117 switch (type) { |
|
| 118 case JSON_NODE_ARRAY: |
|
| 119 fb_json_bldr_arr_begin(bldr, NULL); |
|
| 120 break; |
|
| 121 |
|
| 122 case JSON_NODE_OBJECT: |
|
| 123 fb_json_bldr_obj_begin(bldr, NULL); |
|
| 124 break; |
|
| 125 |
|
| 126 default: |
|
| 127 break; |
|
| 128 } |
|
| 129 |
|
| 130 return bldr; |
|
| 131 } |
|
| 132 |
|
| 133 gchar * |
|
| 134 fb_json_bldr_close(JsonBuilder *bldr, JsonNodeType type, gsize *size) |
|
| 135 { |
|
| 136 gchar *ret; |
|
| 137 JsonGenerator *genr; |
|
| 138 JsonNode *root; |
|
| 139 |
|
| 140 switch (type) { |
|
| 141 case JSON_NODE_ARRAY: |
|
| 142 fb_json_bldr_arr_end(bldr); |
|
| 143 break; |
|
| 144 |
|
| 145 case JSON_NODE_OBJECT: |
|
| 146 fb_json_bldr_obj_end(bldr); |
|
| 147 break; |
|
| 148 |
|
| 149 default: |
|
| 150 break; |
|
| 151 } |
|
| 152 |
|
| 153 genr = json_generator_new(); |
|
| 154 root = json_builder_get_root(bldr); |
|
| 155 |
|
| 156 json_generator_set_root(genr, root); |
|
| 157 ret = json_generator_to_data(genr, size); |
|
| 158 |
|
| 159 json_node_free(root); |
|
| 160 g_object_unref(genr); |
|
| 161 g_object_unref(bldr); |
|
| 162 |
|
| 163 return ret; |
|
| 164 } |
|
| 165 |
|
| 166 void |
|
| 167 fb_json_bldr_arr_begin(JsonBuilder *bldr, const gchar *name) |
|
| 168 { |
|
| 169 if (name != NULL) { |
|
| 170 json_builder_set_member_name(bldr, name); |
|
| 171 } |
|
| 172 |
|
| 173 json_builder_begin_array(bldr); |
|
| 174 } |
|
| 175 |
|
| 176 void |
|
| 177 fb_json_bldr_arr_end(JsonBuilder *bldr) |
|
| 178 { |
|
| 179 json_builder_end_array(bldr); |
|
| 180 } |
|
| 181 |
|
| 182 void |
|
| 183 fb_json_bldr_obj_begin(JsonBuilder *bldr, const gchar *name) |
|
| 184 { |
|
| 185 if (name != NULL) { |
|
| 186 json_builder_set_member_name(bldr, name); |
|
| 187 } |
|
| 188 |
|
| 189 json_builder_begin_object(bldr); |
|
| 190 } |
|
| 191 |
|
| 192 void |
|
| 193 fb_json_bldr_obj_end(JsonBuilder *bldr) |
|
| 194 { |
|
| 195 json_builder_end_object(bldr); |
|
| 196 } |
|
| 197 |
|
| 198 void |
|
| 199 fb_json_bldr_add_bool(JsonBuilder *bldr, const gchar *name, gboolean value) |
|
| 200 { |
|
| 201 if (name != NULL) { |
|
| 202 json_builder_set_member_name(bldr, name); |
|
| 203 } |
|
| 204 |
|
| 205 json_builder_add_boolean_value(bldr, value); |
|
| 206 } |
|
| 207 |
|
| 208 void |
|
| 209 fb_json_bldr_add_dbl(JsonBuilder *bldr, const gchar *name, gdouble value) |
|
| 210 { |
|
| 211 if (name != NULL) { |
|
| 212 json_builder_set_member_name(bldr, name); |
|
| 213 } |
|
| 214 |
|
| 215 json_builder_add_double_value(bldr, value); |
|
| 216 } |
|
| 217 |
|
| 218 void |
|
| 219 fb_json_bldr_add_int(JsonBuilder *bldr, const gchar *name, gint64 value) |
|
| 220 { |
|
| 221 if (name != NULL) { |
|
| 222 json_builder_set_member_name(bldr, name); |
|
| 223 } |
|
| 224 |
|
| 225 json_builder_add_int_value(bldr, value); |
|
| 226 } |
|
| 227 |
|
| 228 void |
|
| 229 fb_json_bldr_add_str(JsonBuilder *bldr, const gchar *name, const gchar *value) |
|
| 230 { |
|
| 231 if (name != NULL) { |
|
| 232 json_builder_set_member_name(bldr, name); |
|
| 233 } |
|
| 234 |
|
| 235 json_builder_add_string_value(bldr, value); |
|
| 236 } |
|
| 237 |
|
| 238 void |
|
| 239 fb_json_bldr_add_strf(JsonBuilder *bldr, const gchar *name, |
|
| 240 const gchar *format, ...) |
|
| 241 { |
|
| 242 gchar *value; |
|
| 243 va_list ap; |
|
| 244 |
|
| 245 va_start(ap, format); |
|
| 246 value = g_strdup_vprintf(format, ap); |
|
| 247 va_end(ap); |
|
| 248 |
|
| 249 fb_json_bldr_add_str(bldr, name, value); |
|
| 250 g_free(value); |
|
| 251 } |
|
| 252 |
|
| 253 JsonNode * |
|
| 254 fb_json_node_new(const gchar *data, gssize size, GError **error) |
|
| 255 { |
|
| 256 gchar *slice; |
|
| 257 JsonNode *root; |
|
| 258 JsonParser *prsr; |
|
| 259 |
|
| 260 g_return_val_if_fail(data != NULL, NULL); |
|
| 261 |
|
| 262 if (size < 0) { |
|
| 263 size = strlen(data); |
|
| 264 } |
|
| 265 |
|
| 266 /* Ensure data is null terminated for json-glib < 1.0.2 */ |
|
| 267 slice = g_strndup(data, size); |
|
| 268 prsr = json_parser_new(); |
|
| 269 |
|
| 270 if (!json_parser_load_from_data(prsr, slice, size, error)) { |
|
| 271 g_object_unref(prsr); |
|
| 272 g_free(slice); |
|
| 273 return NULL; |
|
| 274 } |
|
| 275 |
|
| 276 root = json_parser_get_root(prsr); |
|
| 277 root = json_node_copy(root); |
|
| 278 |
|
| 279 g_object_unref(prsr); |
|
| 280 g_free(slice); |
|
| 281 return root; |
|
| 282 } |
|
| 283 |
|
| 284 JsonNode * |
|
| 285 fb_json_node_get(JsonNode *root, const gchar *expr, GError **error) |
|
| 286 { |
|
| 287 GError *err = NULL; |
|
| 288 guint size; |
|
| 289 JsonArray *rslt; |
|
| 290 JsonNode *node; |
|
| 291 JsonNode *ret; |
|
| 292 |
|
| 293 /* Special case for json-glib < 0.99.2 */ |
|
| 294 if (purple_strequal(expr, "$")) { |
|
| 295 return json_node_copy(root); |
|
| 296 } |
|
| 297 |
|
| 298 node = json_path_query(expr, root, &err); |
|
| 299 |
|
| 300 if (err != NULL) { |
|
| 301 g_propagate_error(error, err); |
|
| 302 json_node_free(node); |
|
| 303 return NULL; |
|
| 304 } |
|
| 305 |
|
| 306 rslt = json_node_get_array(node); |
|
| 307 size = json_array_get_length(rslt); |
|
| 308 |
|
| 309 if (size < 1) { |
|
| 310 g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_NOMATCH, |
|
| 311 _("No matches for %s"), expr); |
|
| 312 json_node_free(node); |
|
| 313 return NULL; |
|
| 314 } |
|
| 315 |
|
| 316 if (size > 1) { |
|
| 317 g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_AMBIGUOUS, |
|
| 318 _("Ambiguous matches for %s"), expr); |
|
| 319 json_node_free(node); |
|
| 320 return NULL; |
|
| 321 } |
|
| 322 |
|
| 323 if (json_array_get_null_element(rslt, 0)) { |
|
| 324 g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_NULL, |
|
| 325 _("Null value for %s"), expr); |
|
| 326 json_node_free(node); |
|
| 327 return NULL; |
|
| 328 } |
|
| 329 |
|
| 330 ret = json_array_dup_element(rslt, 0); |
|
| 331 json_node_free(node); |
|
| 332 return ret; |
|
| 333 } |
|
| 334 |
|
| 335 JsonNode * |
|
| 336 fb_json_node_get_nth(JsonNode *root, guint n) |
|
| 337 { |
|
| 338 GList *vals; |
|
| 339 JsonNode *ret; |
|
| 340 JsonObject *obj; |
|
| 341 |
|
| 342 obj = json_node_get_object(root); |
|
| 343 vals = json_object_get_values(obj); |
|
| 344 ret = g_list_nth_data(vals, n); |
|
| 345 |
|
| 346 g_list_free(vals); |
|
| 347 return ret; |
|
| 348 } |
|
| 349 |
|
| 350 JsonArray * |
|
| 351 fb_json_node_get_arr(JsonNode *root, const gchar *expr, GError **error) |
|
| 352 { |
|
| 353 JsonArray *ret; |
|
| 354 JsonNode *rslt; |
|
| 355 |
|
| 356 rslt = fb_json_node_get(root, expr, error); |
|
| 357 |
|
| 358 if (rslt == NULL) { |
|
| 359 return NULL; |
|
| 360 } |
|
| 361 |
|
| 362 ret = json_node_dup_array(rslt); |
|
| 363 json_node_free(rslt); |
|
| 364 return ret; |
|
| 365 } |
|
| 366 |
|
| 367 gboolean |
|
| 368 fb_json_node_get_bool(JsonNode *root, const gchar *expr, GError **error) |
|
| 369 { |
|
| 370 gboolean ret; |
|
| 371 JsonNode *rslt; |
|
| 372 |
|
| 373 rslt = fb_json_node_get(root, expr, error); |
|
| 374 |
|
| 375 if (rslt == NULL) { |
|
| 376 return FALSE; |
|
| 377 } |
|
| 378 |
|
| 379 ret = json_node_get_boolean(rslt); |
|
| 380 json_node_free(rslt); |
|
| 381 return ret; |
|
| 382 } |
|
| 383 |
|
| 384 gdouble |
|
| 385 fb_json_node_get_dbl(JsonNode *root, const gchar *expr, GError **error) |
|
| 386 { |
|
| 387 gdouble ret; |
|
| 388 JsonNode *rslt; |
|
| 389 |
|
| 390 rslt = fb_json_node_get(root, expr, error); |
|
| 391 |
|
| 392 if (rslt == NULL) { |
|
| 393 return 0.0; |
|
| 394 } |
|
| 395 |
|
| 396 ret = json_node_get_double(rslt); |
|
| 397 json_node_free(rslt); |
|
| 398 return ret; |
|
| 399 } |
|
| 400 |
|
| 401 gint64 |
|
| 402 fb_json_node_get_int(JsonNode *root, const gchar *expr, GError **error) |
|
| 403 { |
|
| 404 gint64 ret; |
|
| 405 JsonNode *rslt; |
|
| 406 |
|
| 407 rslt = fb_json_node_get(root, expr, error); |
|
| 408 |
|
| 409 if (rslt == NULL) { |
|
| 410 return 0; |
|
| 411 } |
|
| 412 |
|
| 413 ret = json_node_get_int(rslt); |
|
| 414 json_node_free(rslt); |
|
| 415 return ret; |
|
| 416 } |
|
| 417 |
|
| 418 gchar * |
|
| 419 fb_json_node_get_str(JsonNode *root, const gchar *expr, GError **error) |
|
| 420 { |
|
| 421 gchar *ret; |
|
| 422 JsonNode *rslt; |
|
| 423 |
|
| 424 rslt = fb_json_node_get(root, expr, error); |
|
| 425 |
|
| 426 if (rslt == NULL) { |
|
| 427 return NULL; |
|
| 428 } |
|
| 429 |
|
| 430 ret = json_node_dup_string(rslt); |
|
| 431 json_node_free(rslt); |
|
| 432 return ret; |
|
| 433 } |
|
| 434 |
|
| 435 FbJsonValues * |
|
| 436 fb_json_values_new(JsonNode *root) |
|
| 437 { |
|
| 438 FbJsonValues *values; |
|
| 439 |
|
| 440 g_return_val_if_fail(root != NULL, NULL); |
|
| 441 |
|
| 442 values = g_object_new(FB_TYPE_JSON_VALUES, NULL); |
|
| 443 values->root = root; |
|
| 444 |
|
| 445 return values; |
|
| 446 } |
|
| 447 |
|
| 448 void |
|
| 449 fb_json_values_add(FbJsonValues *values, FbJsonType type, gboolean required, |
|
| 450 const gchar *expr) |
|
| 451 { |
|
| 452 FbJsonValue *value; |
|
| 453 |
|
| 454 g_return_if_fail(values != NULL); |
|
| 455 g_return_if_fail(expr != NULL); |
|
| 456 |
|
| 457 value = g_new0(FbJsonValue, 1); |
|
| 458 value->expr = expr; |
|
| 459 value->type = type; |
|
| 460 value->required = required; |
|
| 461 |
|
| 462 g_queue_push_tail(values->queue, value); |
|
| 463 } |
|
| 464 |
|
| 465 JsonNode * |
|
| 466 fb_json_values_get_root(FbJsonValues *values) |
|
| 467 { |
|
| 468 guint index; |
|
| 469 |
|
| 470 g_return_val_if_fail(values != NULL, NULL); |
|
| 471 |
|
| 472 if(values->array == NULL) { |
|
| 473 return values->root; |
|
| 474 } |
|
| 475 |
|
| 476 g_return_val_if_fail(values->index > 0, NULL); |
|
| 477 index = values->index - 1; |
|
| 478 |
|
| 479 if(json_array_get_length(values->array) <= index) { |
|
| 480 return NULL; |
|
| 481 } |
|
| 482 |
|
| 483 return json_array_get_element(values->array, index); |
|
| 484 } |
|
| 485 |
|
| 486 void |
|
| 487 fb_json_values_set_array(FbJsonValues *values, gboolean required, |
|
| 488 const gchar *expr) |
|
| 489 { |
|
| 490 g_return_if_fail(values != NULL); |
|
| 491 |
|
| 492 values->array = fb_json_node_get_arr(values->root, expr, &values->error); |
|
| 493 values->isarray = TRUE; |
|
| 494 |
|
| 495 if(!required) { |
|
| 496 g_clear_error(&values->error); |
|
| 497 } |
|
| 498 } |
|
| 499 |
|
| 500 gboolean |
|
| 501 fb_json_values_update(FbJsonValues *values, GError **error) |
|
| 502 { |
|
| 503 FbJsonValue *value; |
|
| 504 GError *err = NULL; |
|
| 505 GList *l; |
|
| 506 GType type; |
|
| 507 JsonNode *root; |
|
| 508 JsonNode *node; |
|
| 509 |
|
| 510 g_return_val_if_fail(values != NULL, FALSE); |
|
| 511 |
|
| 512 if(G_UNLIKELY(values->error != NULL)) { |
|
| 513 g_propagate_error(error, values->error); |
|
| 514 values->error = NULL; |
|
| 515 return FALSE; |
|
| 516 } |
|
| 517 |
|
| 518 if(values->isarray) { |
|
| 519 if(values->array == NULL || |
|
| 520 json_array_get_length(values->array) <= values->index) |
|
| 521 { |
|
| 522 return FALSE; |
|
| 523 } |
|
| 524 |
|
| 525 root = json_array_get_element(values->array, values->index++); |
|
| 526 } else { |
|
| 527 root = values->root; |
|
| 528 } |
|
| 529 |
|
| 530 g_return_val_if_fail(root != NULL, FALSE); |
|
| 531 |
|
| 532 for(l = values->queue->head; l != NULL; l = l->next) { |
|
| 533 value = l->data; |
|
| 534 node = fb_json_node_get(root, value->expr, &err); |
|
| 535 |
|
| 536 if (G_IS_VALUE(&value->value)) { |
|
| 537 g_value_unset(&value->value); |
|
| 538 } |
|
| 539 |
|
| 540 if (err != NULL) { |
|
| 541 json_node_free(node); |
|
| 542 |
|
| 543 if (value->required) { |
|
| 544 g_propagate_error(error, err); |
|
| 545 return FALSE; |
|
| 546 } |
|
| 547 |
|
| 548 g_clear_error(&err); |
|
| 549 continue; |
|
| 550 } |
|
| 551 |
|
| 552 type = json_node_get_value_type(node); |
|
| 553 |
|
| 554 if (G_UNLIKELY(type != value->type)) { |
|
| 555 g_set_error(error, FB_JSON_ERROR, FB_JSON_ERROR_TYPE, |
|
| 556 _("Expected a %s but got a %s for %s"), |
|
| 557 g_type_name(value->type), |
|
| 558 g_type_name(type), |
|
| 559 value->expr); |
|
| 560 json_node_free(node); |
|
| 561 return FALSE; |
|
| 562 } |
|
| 563 |
|
| 564 json_node_get_value(node, &value->value); |
|
| 565 json_node_free(node); |
|
| 566 } |
|
| 567 |
|
| 568 values->next = values->queue->head; |
|
| 569 return TRUE; |
|
| 570 } |
|
| 571 |
|
| 572 const GValue * |
|
| 573 fb_json_values_next(FbJsonValues *values) |
|
| 574 { |
|
| 575 FbJsonValue *value; |
|
| 576 |
|
| 577 g_return_val_if_fail(values != NULL, NULL); |
|
| 578 |
|
| 579 g_return_val_if_fail(values->next != NULL, NULL); |
|
| 580 value = values->next->data; |
|
| 581 values->next = values->next->next; |
|
| 582 |
|
| 583 if (!G_IS_VALUE(&value->value)) { |
|
| 584 return NULL; |
|
| 585 } |
|
| 586 |
|
| 587 return &value->value; |
|
| 588 } |
|
| 589 |
|
| 590 gboolean |
|
| 591 fb_json_values_next_bool(FbJsonValues *values, gboolean defval) |
|
| 592 { |
|
| 593 const GValue *value; |
|
| 594 |
|
| 595 value = fb_json_values_next(values); |
|
| 596 |
|
| 597 if (G_UNLIKELY(value == NULL)) { |
|
| 598 return defval; |
|
| 599 } |
|
| 600 |
|
| 601 return g_value_get_boolean(value); |
|
| 602 } |
|
| 603 |
|
| 604 gdouble |
|
| 605 fb_json_values_next_dbl(FbJsonValues *values, gdouble defval) |
|
| 606 { |
|
| 607 const GValue *value; |
|
| 608 |
|
| 609 value = fb_json_values_next(values); |
|
| 610 |
|
| 611 if (G_UNLIKELY(value == NULL)) { |
|
| 612 return defval; |
|
| 613 } |
|
| 614 |
|
| 615 return g_value_get_double(value); |
|
| 616 } |
|
| 617 |
|
| 618 gint64 |
|
| 619 fb_json_values_next_int(FbJsonValues *values, gint64 defval) |
|
| 620 { |
|
| 621 const GValue *value; |
|
| 622 |
|
| 623 value = fb_json_values_next(values); |
|
| 624 |
|
| 625 if (G_UNLIKELY(value == NULL)) { |
|
| 626 return defval; |
|
| 627 } |
|
| 628 |
|
| 629 return g_value_get_int64(value); |
|
| 630 } |
|
| 631 |
|
| 632 const gchar * |
|
| 633 fb_json_values_next_str(FbJsonValues *values, const gchar *defval) |
|
| 634 { |
|
| 635 const GValue *value; |
|
| 636 |
|
| 637 value = fb_json_values_next(values); |
|
| 638 |
|
| 639 if (G_UNLIKELY(value == NULL)) { |
|
| 640 return defval; |
|
| 641 } |
|
| 642 |
|
| 643 return g_value_get_string(value); |
|
| 644 } |
|
| 645 |
|
| 646 gchar * |
|
| 647 fb_json_values_next_str_dup(FbJsonValues *values, const gchar *defval) |
|
| 648 { |
|
| 649 const GValue *value; |
|
| 650 |
|
| 651 value = fb_json_values_next(values); |
|
| 652 |
|
| 653 if (G_UNLIKELY(value == NULL)) { |
|
| 654 return g_strdup(defval); |
|
| 655 } |
|
| 656 |
|
| 657 return g_value_dup_string(value); |
|
| 658 } |
|