Initial Import
[profile/ivi/json-glib.git] / json-glib / json-generator.c
1 /* json-generator.c - JSON streams generator
2  * 
3  * This file is part of JSON-GLib
4  * Copyright (C) 2007  OpenedHand Ltd.
5  * Copyright (C) 2009  Intel Corp.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  *
20  * Author:
21  *   Emmanuele Bassi  <ebassi@linux.intel.com>
22  */
23
24 /**
25  * SECTION:json-generator
26  * @short_description: Generates JSON data streams
27  *
28  * #JsonGenerator provides an object for generating a JSON data stream and
29  * put it into a buffer or a file.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "json-types-private.h"
40
41 #include "json-marshal.h"
42 #include "json-generator.h"
43
44 #define JSON_GENERATOR_GET_PRIVATE(obj) \
45         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), JSON_TYPE_GENERATOR, JsonGeneratorPrivate))
46
47 struct _JsonGeneratorPrivate
48 {
49   JsonNode *root;
50
51   guint indent;
52   gunichar indent_char;
53
54   guint pretty : 1;
55 };
56
57 enum
58 {
59   PROP_0,
60
61   PROP_PRETTY,
62   PROP_INDENT,
63   PROP_ROOT,
64   PROP_INDENT_CHAR,
65
66   PROP_LAST
67 };
68
69 static gchar *dump_value  (JsonGenerator *generator,
70                            gint           level,
71                            const gchar   *name,
72                            JsonNode      *node,
73                            gsize         *length);
74 static gchar *dump_array  (JsonGenerator *generator,
75                            gint           level,
76                            const gchar   *name,
77                            JsonArray     *array,
78                            gsize         *length);
79 static gchar *dump_object (JsonGenerator *generator,
80                            gint           level,
81                            const gchar   *name,
82                            JsonObject    *object,
83                            gsize         *length);
84
85 /* non-ASCII characters can't be escaped, otherwise UTF-8
86  * chars will break, so we just pregenerate this table of
87  * high characters and then we feed it to g_strescape()
88  */
89 static const char json_exceptions[] = {
90   0x7f,  0x80,  0x81,  0x82,  0x83,  0x84,  0x85,  0x86,
91   0x87,  0x88,  0x89,  0x8a,  0x8b,  0x8c,  0x8d,  0x8e,
92   0x8f,  0x90,  0x91,  0x92,  0x93,  0x94,  0x95,  0x96,
93   0x97,  0x98,  0x99,  0x9a,  0x9b,  0x9c,  0x9d,  0x9e,
94   0x9f,  0xa0,  0xa1,  0xa2,  0xa3,  0xa4,  0xa5,  0xa6,
95   0xa7,  0xa8,  0xa9,  0xaa,  0xab,  0xac,  0xad,  0xae,
96   0xaf,  0xb0,  0xb1,  0xb2,  0xb3,  0xb4,  0xb5,  0xb6,
97   0xb7,  0xb8,  0xb9,  0xba,  0xbb,  0xbc,  0xbd,  0xbe,
98   0xbf,  0xc0,  0xc1,  0xc2,  0xc3,  0xc4,  0xc5,  0xc6,
99   0xc7,  0xc8,  0xc9,  0xca,  0xcb,  0xcc,  0xcd,  0xce,
100   0xcf,  0xd0,  0xd1,  0xd2,  0xd3,  0xd4,  0xd5,  0xd6,
101   0xd7,  0xd8,  0xd9,  0xda,  0xdb,  0xdc,  0xdd,  0xde,
102   0xdf,  0xe0,  0xe1,  0xe2,  0xe3,  0xe4,  0xe5,  0xe6,
103   0xe7,  0xe8,  0xe9,  0xea,  0xeb,  0xec,  0xed,  0xee,
104   0xef,  0xf0,  0xf1,  0xf2,  0xf3,  0xf4,  0xf5,  0xf6,
105   0xf7,  0xf8,  0xf9,  0xfa,  0xfb,  0xfc,  0xfd,  0xfe,
106   0xff,
107   '\0'   /* g_strescape() expects a NUL-terminated string */
108 };
109
110 static GParamSpec *generator_props[PROP_LAST] = { NULL, };
111
112 G_DEFINE_TYPE (JsonGenerator, json_generator, G_TYPE_OBJECT);
113
114 static gchar *
115 json_strescape (const gchar *str)
116 {
117   return g_strescape (str, json_exceptions);
118 }
119
120 static void
121 json_generator_finalize (GObject *gobject)
122 {
123   JsonGeneratorPrivate *priv = JSON_GENERATOR_GET_PRIVATE (gobject);
124
125   if (priv->root)
126     json_node_free (priv->root);
127
128   G_OBJECT_CLASS (json_generator_parent_class)->finalize (gobject);
129 }
130
131 static void
132 json_generator_set_property (GObject      *gobject,
133                              guint         prop_id,
134                              const GValue *value,
135                              GParamSpec   *pspec)
136 {
137   JsonGenerator *generator = JSON_GENERATOR (gobject);
138
139   switch (prop_id)
140     {
141     case PROP_PRETTY:
142       json_generator_set_pretty (generator, g_value_get_boolean (value));
143       break;
144
145     case PROP_INDENT:
146       json_generator_set_indent (generator, g_value_get_uint (value));
147       break;
148
149     case PROP_INDENT_CHAR:
150       json_generator_set_indent_char (generator, g_value_get_uint (value));
151       break;
152
153     case PROP_ROOT:
154       json_generator_set_root (generator, g_value_get_boxed (value));
155       break;
156
157     default:
158       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
159       break;
160     }
161 }
162
163 static void
164 json_generator_get_property (GObject    *gobject,
165                              guint       prop_id,
166                              GValue     *value,
167                              GParamSpec *pspec)
168 {
169   JsonGeneratorPrivate *priv = JSON_GENERATOR (gobject)->priv;
170
171   switch (prop_id)
172     {
173     case PROP_PRETTY:
174       g_value_set_boolean (value, priv->pretty);
175       break;
176     case PROP_INDENT:
177       g_value_set_uint (value, priv->indent);
178       break;
179     case PROP_INDENT_CHAR:
180       g_value_set_uint (value, priv->indent_char);
181       break;
182     case PROP_ROOT:
183       g_value_set_boxed (value, priv->root);
184       break;
185     default:
186       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
187       break;
188     }
189 }
190
191 static void
192 json_generator_class_init (JsonGeneratorClass *klass)
193 {
194   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
195
196   g_type_class_add_private (klass, sizeof (JsonGeneratorPrivate));
197
198   /**
199    * JsonGenerator:pretty:
200    *
201    * Whether the output should be "pretty-printed", with indentation and
202    * newlines. The indentation level can be controlled by using the
203    * JsonGenerator:indent property
204    */
205   generator_props[PROP_PRETTY] =
206     g_param_spec_boolean ("pretty",
207                           "Pretty",
208                           "Pretty-print the output",
209                           FALSE,
210                           G_PARAM_READWRITE);
211
212   /**
213    * JsonGenerator:indent:
214    *
215    * Number of spaces to be used to indent when pretty printing.
216    */
217   generator_props[PROP_INDENT] =
218     g_param_spec_uint ("indent",
219                        "Indent",
220                        "Number of indentation spaces",
221                        0, G_MAXUINT,
222                        2,
223                        G_PARAM_READWRITE);
224
225   /**
226    * JsonGenerator:root:
227    *
228    * The root #JsonNode to be used when constructing a JSON data
229    * stream.
230    *
231    * Since: 0.4
232    */
233   generator_props[PROP_ROOT] =
234     g_param_spec_boxed ("root",
235                         "Root",
236                         "Root of the JSON data tree",
237                         JSON_TYPE_NODE,
238                         G_PARAM_READWRITE);
239
240   /**
241    * JsonGenerator:indent-char:
242    *
243    * The character that should be used when indenting in pretty print.
244    *
245    * Since: 0.6
246    */
247   generator_props[PROP_INDENT_CHAR] =
248     g_param_spec_unichar ("indent-char",
249                           "Indent Char",
250                           "Character that should be used when indenting",
251                           ' ',
252                           G_PARAM_READWRITE);
253
254   gobject_class->set_property = json_generator_set_property;
255   gobject_class->get_property = json_generator_get_property;
256   gobject_class->finalize = json_generator_finalize;
257   g_object_class_install_properties (gobject_class, PROP_LAST, generator_props);
258 }
259
260 static void
261 json_generator_init (JsonGenerator *generator)
262 {
263   JsonGeneratorPrivate *priv;
264
265   generator->priv = priv = JSON_GENERATOR_GET_PRIVATE (generator);
266
267   priv->pretty = FALSE;
268   priv->indent = 2;
269   priv->indent_char = ' ';
270 }
271
272 static gchar *
273 dump_value (JsonGenerator *generator,
274             gint           level,
275             const gchar   *name,
276             JsonNode      *node,
277             gsize         *length)
278 {
279   JsonGeneratorPrivate *priv = generator->priv;
280   gboolean pretty = priv->pretty;
281   guint indent = priv->indent;
282   GValue value = { 0, };
283   GString *buffer;
284
285   buffer = g_string_new ("");
286
287   if (pretty)
288     {
289       guint i;
290
291       for (i = 0; i < (level * indent); i++)
292         g_string_append_c (buffer, priv->indent_char);
293     }
294
295   if (name && name[0] != '\0')
296     {
297       if (pretty)
298         g_string_append_printf (buffer, "\"%s\" : ", name);
299       else
300         g_string_append_printf (buffer, "\"%s\":", name);
301     }
302
303   json_node_get_value (node, &value);
304
305   switch (G_VALUE_TYPE (&value))
306     {
307     case G_TYPE_INT64:
308       g_string_append_printf (buffer, "%" G_GINT64_FORMAT, g_value_get_int64 (&value));
309       break;
310
311     case G_TYPE_STRING:
312       {
313         gchar *tmp;
314
315         tmp = json_strescape (g_value_get_string (&value));
316         g_string_append_printf (buffer, "\"%s\"", tmp);
317
318         g_free (tmp);
319       }
320       break;
321
322     case G_TYPE_DOUBLE:
323       {
324         gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
325
326         g_string_append (buffer,
327                          g_ascii_dtostr (buf, sizeof (buf),
328                                          g_value_get_double (&value)));
329       }
330       break;
331
332     case G_TYPE_BOOLEAN:
333       g_string_append_printf (buffer, "%s",
334                               g_value_get_boolean (&value) ? "true" : "false");
335       break;
336
337     default:
338       break;
339     }
340
341   g_value_unset (&value);
342
343   if (length)
344     *length = buffer->len;
345
346   return g_string_free (buffer, FALSE);
347 }
348
349 static gchar *
350 dump_array (JsonGenerator *generator,
351             gint           level,
352             const gchar   *name,
353             JsonArray     *array,
354             gsize         *length)
355 {
356   JsonGeneratorPrivate *priv = generator->priv;
357   guint array_len = json_array_get_length (array);
358   guint i;
359   GString *buffer;
360   gboolean pretty = priv->pretty;
361   guint indent = priv->indent;
362
363   buffer = g_string_new ("");
364
365   if (pretty)
366     {
367       for (i = 0; i < (level * indent); i++)
368         g_string_append_c (buffer, priv->indent_char);
369     }
370
371   if (name && name[0] != '\0')
372     {
373       if (pretty)
374         g_string_append_printf (buffer, "\"%s\" : ", name);
375       else
376         g_string_append_printf (buffer, "\"%s\":", name);
377     }
378
379   g_string_append_c (buffer, '[');
380
381   if (pretty)
382     g_string_append_c (buffer, '\n');
383
384   for (i = 0; i < array_len; i++)
385     {
386       JsonNode *cur = json_array_get_element (array, i);
387       guint sub_level = level + 1;
388       guint j;
389       gchar *value; 
390
391       switch (JSON_NODE_TYPE (cur))
392         {
393         case JSON_NODE_NULL:
394           if (pretty)
395             {
396               for (j = 0; j < (sub_level * indent); j++)
397                 g_string_append_c (buffer, priv->indent_char);
398             }
399           g_string_append (buffer, "null");
400           break;
401
402         case JSON_NODE_VALUE:
403           value = dump_value (generator, sub_level, NULL, cur, NULL);
404           g_string_append (buffer, value);
405           g_free (value);
406           break;
407
408         case JSON_NODE_ARRAY:
409           value = dump_array (generator, sub_level, NULL, json_node_get_array (cur), NULL);
410           g_string_append (buffer, value);
411           g_free (value);
412           break;
413
414         case JSON_NODE_OBJECT:
415           value = dump_object (generator, sub_level, NULL, json_node_get_object (cur), NULL);
416           g_string_append (buffer, value);
417           g_free (value);
418           break;
419         }
420
421       if ((i + 1) != array_len)
422         g_string_append_c (buffer, ',');
423
424       if (pretty)
425         g_string_append_c (buffer, '\n');
426     }
427
428   if (pretty)
429     {
430       for (i = 0; i < (level * indent); i++)
431         g_string_append_c (buffer, priv->indent_char);
432     }
433
434   g_string_append_c (buffer, ']');
435
436   if (length)
437     *length = buffer->len;
438
439   return g_string_free (buffer, FALSE);
440 }
441
442 static gchar *
443 dump_object (JsonGenerator *generator,
444              gint           level,
445              const gchar   *name,
446              JsonObject    *object,
447              gsize         *length)
448 {
449   JsonGeneratorPrivate *priv = generator->priv;
450   GList *members, *l;
451   GString *buffer;
452   gboolean pretty = priv->pretty;
453   guint indent = priv->indent;
454   guint i;
455
456   buffer = g_string_new ("");
457
458   if (pretty)
459     {
460       for (i = 0; i < (level * indent); i++)
461         g_string_append_c (buffer, priv->indent_char);
462     }
463
464   if (name && name[0] != '\0')
465     {
466       if (pretty)
467         g_string_append_printf (buffer, "\"%s\" : ", name);
468       else
469         g_string_append_printf (buffer, "\"%s\":", name);
470     }
471
472   g_string_append_c (buffer, '{');
473
474   if (pretty)
475     g_string_append_c (buffer, '\n');
476
477   members = json_object_get_members (object);
478
479   for (l = members; l != NULL; l = l->next)
480     {
481       const gchar *member_name = l->data;
482       JsonNode *cur = json_object_get_member (object, member_name);
483       guint sub_level = level + 1;
484       guint j;
485       gchar *value;
486
487       switch (JSON_NODE_TYPE (cur))
488         {
489         case JSON_NODE_NULL:
490           if (pretty)
491             {
492               for (j = 0; j < (sub_level * indent); j++)
493                 g_string_append_c (buffer, priv->indent_char);
494               g_string_append_printf (buffer, "\"%s\" : null", member_name);
495             }
496           else
497             {
498               g_string_append_printf (buffer, "\"%s\":null", member_name);
499             }
500           break;
501
502         case JSON_NODE_VALUE:
503           value = dump_value (generator, sub_level, member_name, cur, NULL);
504           g_string_append (buffer, value);
505           g_free (value);
506           break;
507
508         case JSON_NODE_ARRAY:
509           value = dump_array (generator, sub_level, member_name,
510                               json_node_get_array (cur), NULL);
511           g_string_append (buffer, value);
512           g_free (value);
513           break;
514
515         case JSON_NODE_OBJECT:
516           value = dump_object (generator, sub_level, member_name,
517                                json_node_get_object (cur), NULL);
518           g_string_append (buffer, value);
519           g_free (value);
520           break;
521         }
522
523       if (l->next != NULL)
524         g_string_append_c (buffer, ',');
525
526       if (pretty)
527         g_string_append_c (buffer, '\n');
528     }
529
530   g_list_free (members);
531
532   if (pretty)
533     {
534       for (i = 0; i < (level * indent); i++)
535         g_string_append_c (buffer, priv->indent_char);
536     }
537
538   g_string_append_c (buffer, '}');
539
540   if (length)
541     *length = buffer->len;
542
543   return g_string_free (buffer, FALSE);
544 }
545
546 /**
547  * json_generator_new:
548  * 
549  * Creates a new #JsonGenerator. You can use this object to generate a
550  * JSON data stream starting from a data object model composed by
551  * #JsonNode<!-- -->s.
552  *
553  * Return value: the newly created #JsonGenerator instance
554  */
555 JsonGenerator *
556 json_generator_new (void)
557 {
558   return g_object_new (JSON_TYPE_GENERATOR, NULL);
559 }
560
561 /**
562  * json_generator_to_data:
563  * @generator: a #JsonGenerator
564  * @length: (out): return location for the length of the returned
565  *   buffer, or %NULL
566  *
567  * Generates a JSON data stream from @generator and returns it as a
568  * buffer.
569  *
570  * Return value: a newly allocated buffer holding a JSON data stream.
571  *   Use g_free() to free the allocated resources.
572  */
573 gchar *
574 json_generator_to_data (JsonGenerator *generator,
575                         gsize         *length)
576 {
577   JsonNode *root;
578   gchar *retval = NULL;
579
580   g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL);
581
582   root = generator->priv->root;
583   if (!root)
584     {
585       if (length)
586         *length = 0;
587
588       return NULL;
589     }
590
591   switch (JSON_NODE_TYPE (root))
592     {
593     case JSON_NODE_ARRAY:
594       retval = dump_array (generator, 0, NULL, json_node_get_array (root), length);
595       break;
596
597     case JSON_NODE_OBJECT:
598       retval = dump_object (generator, 0, NULL, json_node_get_object (root), length);
599       break;
600
601     case JSON_NODE_NULL:
602       retval = g_strdup ("null");
603       if (length)
604         *length = 4;
605       break;
606
607     case JSON_NODE_VALUE:
608       retval = dump_value (generator, 0, NULL, root, length);
609       break;
610     }
611
612   return retval;
613 }
614
615 /**
616  * json_generator_to_file:
617  * @generator: a #JsonGenerator
618  * @filename: path to the target file
619  * @error: return location for a #GError, or %NULL
620  *
621  * Creates a JSON data stream and puts it inside @filename, overwriting the
622  * current file contents. This operation is atomic.
623  *
624  * Return value: %TRUE if saving was successful.
625  */
626 gboolean
627 json_generator_to_file (JsonGenerator  *generator,
628                         const gchar    *filename,
629                         GError        **error)
630 {
631   gchar *buffer;
632   gsize len;
633   gboolean retval;
634
635   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
636   g_return_val_if_fail (filename != NULL, FALSE);
637
638   buffer = json_generator_to_data (generator, &len);
639   retval = g_file_set_contents (filename, buffer, len, error);
640   g_free (buffer);
641
642   return retval;
643 }
644
645 /**
646  * json_generator_to_stream:
647  * @generator: a #JsonGenerator
648  * @stream: a #GOutputStream
649  * @cancellable: (allow-none): a #GCancellable, or %NULL
650  * @error: return location for a #GError, or %NULL
651  *
652  * Outputs JSON data and streams it (synchronously) to @stream.
653  *
654  * Return value: %TRUE if the write operation was successful, and %FALSE
655  *   on failure. In case of error, the #GError will be filled accordingly
656  *
657  * Since: 0.12
658  */
659 gboolean
660 json_generator_to_stream (JsonGenerator  *generator,
661                           GOutputStream  *stream,
662                           GCancellable   *cancellable,
663                           GError        **error)
664 {
665   gboolean retval;
666   gchar *buffer;
667   gsize len;
668
669   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
670   g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
671
672   if (g_cancellable_set_error_if_cancelled (cancellable, error))
673     return FALSE;
674
675   buffer = json_generator_to_data (generator, &len);
676   retval = g_output_stream_write (stream, buffer, len, cancellable, error);
677   g_free (buffer);
678
679   return retval;
680 }
681
682 /**
683  * json_generator_set_root:
684  * @generator: a #JsonGenerator
685  * @node: a #JsonNode
686  *
687  * Sets @node as the root of the JSON data stream to be serialized by
688  * the #JsonGenerator.
689  *
690  * <note>The node is copied by the generator object, so it can be safely
691  * freed after calling this function.</note>
692  */
693 void
694 json_generator_set_root (JsonGenerator *generator,
695                          JsonNode      *node)
696 {
697   g_return_if_fail (JSON_IS_GENERATOR (generator));
698
699   if (generator->priv->root != NULL)
700     {
701       json_node_free (generator->priv->root);
702       generator->priv->root = NULL;
703     }
704
705   if (node != NULL)
706     generator->priv->root = json_node_copy (node);
707
708   g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_ROOT]);
709 }
710
711 /**
712  * json_generator_get_root:
713  * @generator: a #JsonGenerator
714  *
715  * Retrieves a pointer to the root #JsonNode set using
716  * json_generator_set_root().
717  *
718  * Return value: (transfer none): a #JsonNode, or %NULL. The returned node
719  *   is owned by the #JsonGenerator and it should not be freed
720  *
721  * Since: 0.14
722  */
723 JsonNode *
724 json_generator_get_root (JsonGenerator *generator)
725 {
726   g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL);
727
728   return generator->priv->root;
729 }
730
731 /**
732  * json_generator_set_pretty:
733  * @generator: a #JsonGenerator
734  * @is_pretty: whether the generated string should be pretty printed
735  *
736  * Sets whether the generated JSON should be pretty printed, using the
737  * indentation character specified in the #JsonGenerator:indent-char
738  * property and the spacing specified in #JsonGenerator:indent property.
739  *
740  * Since: 0.14
741  */
742 void
743 json_generator_set_pretty (JsonGenerator *generator,
744                            gboolean       is_pretty)
745 {
746   JsonGeneratorPrivate *priv;
747
748   g_return_if_fail (JSON_IS_GENERATOR (generator));
749
750   priv = generator->priv;
751
752   is_pretty = !!is_pretty;
753
754   if (priv->pretty != is_pretty)
755     {
756       priv->pretty = is_pretty;
757
758       g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_PRETTY]);
759     }
760 }
761
762 /**
763  * json_generator_get_pretty:
764  * @generator: a #JsonGenerator
765  *
766  * Retrieves the value set using json_generator_set_pretty().
767  *
768  * Return value: %TRUE if the generated JSON should be pretty-printed, and
769  *   %FALSE otherwise
770  *
771  * Since: 0.14
772  */
773 gboolean
774 json_generator_get_pretty (JsonGenerator *generator)
775 {
776   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
777
778   return generator->priv->pretty;
779 }
780
781 /**
782  * json_generator_set_indent:
783  * @generator: a #JsonGenerator
784  * @indent_level: the number of repetitions of the indentation character
785  *   that should be applied when pretty printing
786  *
787  * Sets the number of repetitions for each indentation level.
788  *
789  * Since: 0.14
790  */
791 void
792 json_generator_set_indent (JsonGenerator *generator,
793                            guint          indent_level)
794 {
795   JsonGeneratorPrivate *priv;
796
797   g_return_if_fail (JSON_IS_GENERATOR (generator));
798
799   priv = generator->priv;
800
801   if (priv->indent != indent_level)
802     {
803       priv->indent = indent_level;
804
805       g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_INDENT]);
806     }
807 }
808
809 /**
810  * json_generator_get_indent:
811  * @generator: a #JsonGenerator
812  *
813  * Retrieves the value set using json_generator_set_indent().
814  *
815  * Return value: the number of repetitions per indentation level
816  *
817  * Since: 0.14
818  */
819 guint
820 json_generator_get_indent (JsonGenerator *generator)
821 {
822   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
823
824   return generator->priv->indent;
825 }
826
827 /**
828  * json_generator_set_indent_char:
829  * @generator: a #JsonGenerator
830  * @indent_char: a Unicode character to be used when indenting
831  *
832  * Sets the character to be used when indenting
833  *
834  * Since: 0.14
835  */
836 void
837 json_generator_set_indent_char (JsonGenerator *generator,
838                                 gunichar       indent_char)
839 {
840   JsonGeneratorPrivate *priv;
841
842   g_return_if_fail (JSON_IS_GENERATOR (generator));
843
844   priv = generator->priv;
845
846   if (priv->indent_char != indent_char)
847     {
848       priv->indent_char = indent_char;
849
850       g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_INDENT_CHAR]);
851     }
852 }
853
854 /**
855  * json_generator_get_indent_char:
856  * @generator: a #JsonGenerator
857  *
858  * Retrieves the value set using json_generator_set_indent_char().
859  *
860  * Return value: the character to be used when indenting
861  *
862  * Since: 0.14
863  */
864 gunichar
865 json_generator_get_indent_char (JsonGenerator *generator)
866 {
867   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
868
869   return generator->priv->indent_char;
870 }