tizen 2.4 release
[external/libjson-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 #include "config.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "json-types-private.h"
38
39 #include "json-marshal.h"
40 #include "json-generator.h"
41
42 struct _JsonGeneratorPrivate
43 {
44   JsonNode *root;
45
46   guint indent;
47   gunichar indent_char;
48
49   guint pretty : 1;
50 };
51
52 enum
53 {
54   PROP_0,
55
56   PROP_PRETTY,
57   PROP_INDENT,
58   PROP_ROOT,
59   PROP_INDENT_CHAR,
60
61   PROP_LAST
62 };
63
64 static gchar *dump_value  (JsonGenerator *generator,
65                            gint           level,
66                            const gchar   *name,
67                            JsonNode      *node,
68                            gsize         *length);
69 static gchar *dump_array  (JsonGenerator *generator,
70                            gint           level,
71                            const gchar   *name,
72                            JsonArray     *array,
73                            gsize         *length);
74 static gchar *dump_object (JsonGenerator *generator,
75                            gint           level,
76                            const gchar   *name,
77                            JsonObject    *object,
78                            gsize         *length);
79
80 static GParamSpec *generator_props[PROP_LAST] = { NULL, };
81
82 G_DEFINE_TYPE_WITH_PRIVATE (JsonGenerator, json_generator, G_TYPE_OBJECT)
83
84 static gchar *
85 json_strescape (const gchar *str)
86 {
87   const gchar *p;
88   const gchar *end;
89   GString *output;
90   gsize len;
91
92   len = strlen (str);
93   end = str + len;
94   output = g_string_sized_new (len);
95
96   for (p = str; p < end; p++)
97     {
98       if (*p == '\\' || *p == '"')
99         {
100           g_string_append_c (output, '\\');
101           g_string_append_c (output, *p);
102         }
103       else if ((*p > 0 && *p < 0x1f) || *p == 0x7f)
104         {
105           switch (*p)
106             {
107             case '\b':
108               g_string_append (output, "\\b");
109               break;
110             case '\f':
111               g_string_append (output, "\\f");
112               break;
113             case '\n':
114               g_string_append (output, "\\n");
115               break;
116             case '\r':
117               g_string_append (output, "\\r");
118               break;
119             case '\t':
120               g_string_append (output, "\\t");
121               break;
122             default:
123               g_string_append_printf (output, "\\u00%02x", (guint)*p);
124               break;
125             }
126         }
127       else
128         {
129           g_string_append_c (output, *p);
130         }
131     }
132
133   return g_string_free (output, FALSE);
134 }
135
136 static void
137 json_generator_finalize (GObject *gobject)
138 {
139   JsonGeneratorPrivate *priv;
140
141   priv = json_generator_get_instance_private ((JsonGenerator *) gobject);
142   if (priv->root != NULL)
143     json_node_free (priv->root);
144
145   G_OBJECT_CLASS (json_generator_parent_class)->finalize (gobject);
146 }
147
148 static void
149 json_generator_set_property (GObject      *gobject,
150                              guint         prop_id,
151                              const GValue *value,
152                              GParamSpec   *pspec)
153 {
154   JsonGenerator *generator = JSON_GENERATOR (gobject);
155
156   switch (prop_id)
157     {
158     case PROP_PRETTY:
159       json_generator_set_pretty (generator, g_value_get_boolean (value));
160       break;
161
162     case PROP_INDENT:
163       json_generator_set_indent (generator, g_value_get_uint (value));
164       break;
165
166     case PROP_INDENT_CHAR:
167       json_generator_set_indent_char (generator, g_value_get_uint (value));
168       break;
169
170     case PROP_ROOT:
171       json_generator_set_root (generator, g_value_get_boxed (value));
172       break;
173
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
176       break;
177     }
178 }
179
180 static void
181 json_generator_get_property (GObject    *gobject,
182                              guint       prop_id,
183                              GValue     *value,
184                              GParamSpec *pspec)
185 {
186   JsonGeneratorPrivate *priv = JSON_GENERATOR (gobject)->priv;
187
188   switch (prop_id)
189     {
190     case PROP_PRETTY:
191       g_value_set_boolean (value, priv->pretty);
192       break;
193     case PROP_INDENT:
194       g_value_set_uint (value, priv->indent);
195       break;
196     case PROP_INDENT_CHAR:
197       g_value_set_uint (value, priv->indent_char);
198       break;
199     case PROP_ROOT:
200       g_value_set_boxed (value, priv->root);
201       break;
202     default:
203       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
204       break;
205     }
206 }
207
208 static void
209 json_generator_class_init (JsonGeneratorClass *klass)
210 {
211   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
212
213   /**
214    * JsonGenerator:pretty:
215    *
216    * Whether the output should be "pretty-printed", with indentation and
217    * newlines. The indentation level can be controlled by using the
218    * JsonGenerator:indent property
219    */
220   generator_props[PROP_PRETTY] =
221     g_param_spec_boolean ("pretty",
222                           "Pretty",
223                           "Pretty-print the output",
224                           FALSE,
225                           G_PARAM_READWRITE);
226
227   /**
228    * JsonGenerator:indent:
229    *
230    * Number of spaces to be used to indent when pretty printing.
231    */
232   generator_props[PROP_INDENT] =
233     g_param_spec_uint ("indent",
234                        "Indent",
235                        "Number of indentation spaces",
236                        0, G_MAXUINT,
237                        2,
238                        G_PARAM_READWRITE);
239
240   /**
241    * JsonGenerator:root:
242    *
243    * The root #JsonNode to be used when constructing a JSON data
244    * stream.
245    *
246    * Since: 0.4
247    */
248   generator_props[PROP_ROOT] =
249     g_param_spec_boxed ("root",
250                         "Root",
251                         "Root of the JSON data tree",
252                         JSON_TYPE_NODE,
253                         G_PARAM_READWRITE);
254
255   /**
256    * JsonGenerator:indent-char:
257    *
258    * The character that should be used when indenting in pretty print.
259    *
260    * Since: 0.6
261    */
262   generator_props[PROP_INDENT_CHAR] =
263     g_param_spec_unichar ("indent-char",
264                           "Indent Char",
265                           "Character that should be used when indenting",
266                           ' ',
267                           G_PARAM_READWRITE);
268
269   gobject_class->set_property = json_generator_set_property;
270   gobject_class->get_property = json_generator_get_property;
271   gobject_class->finalize = json_generator_finalize;
272   g_object_class_install_properties (gobject_class, PROP_LAST, generator_props);
273 }
274
275 static void
276 json_generator_init (JsonGenerator *generator)
277 {
278   JsonGeneratorPrivate *priv = json_generator_get_instance_private (generator);
279
280   generator->priv = priv;
281
282   priv->pretty = FALSE;
283   priv->indent = 2;
284   priv->indent_char = ' ';
285 }
286
287 static gchar *
288 dump_value (JsonGenerator *generator,
289             gint           level,
290             const gchar   *name,
291             JsonNode      *node,
292             gsize         *length)
293 {
294   JsonGeneratorPrivate *priv = generator->priv;
295   gboolean pretty = priv->pretty;
296   guint indent = priv->indent;
297   const JsonValue *value;
298   GString *buffer;
299
300   buffer = g_string_new ("");
301
302   if (pretty)
303     {
304       guint i;
305
306       for (i = 0; i < (level * indent); i++)
307         g_string_append_c (buffer, priv->indent_char);
308     }
309
310   if (name)
311     {
312       if (pretty)
313         g_string_append_printf (buffer, "\"%s\" : ", name);
314       else
315         g_string_append_printf (buffer, "\"%s\":", name);
316     }
317
318   value = node->data.value;
319
320   switch (value->type)
321     {
322     case JSON_VALUE_INT:
323       g_string_append_printf (buffer, "%" G_GINT64_FORMAT, json_value_get_int (value));
324       break;
325
326     case JSON_VALUE_STRING:
327       {
328         gchar *tmp;
329
330         tmp = json_strescape (json_value_get_string (value));
331         g_string_append_c (buffer, '"');
332         g_string_append (buffer, tmp);
333         g_string_append_c (buffer, '"');
334
335         g_free (tmp);
336       }
337       break;
338
339     case JSON_VALUE_DOUBLE:
340       {
341         gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
342
343         g_string_append (buffer,
344                          g_ascii_dtostr (buf, sizeof (buf),
345                                          json_value_get_double (value)));
346       }
347       break;
348
349     case JSON_VALUE_BOOLEAN:
350       g_string_append (buffer, json_value_get_boolean (value) ? "true" : "false");
351       break;
352
353     case JSON_VALUE_NULL:
354       g_string_append (buffer, "null");
355       break;
356
357     default:
358       break;
359     }
360
361   if (length)
362     *length = buffer->len;
363
364   return g_string_free (buffer, FALSE);
365 }
366
367 static gchar *
368 dump_array (JsonGenerator *generator,
369             gint           level,
370             const gchar   *name,
371             JsonArray     *array,
372             gsize         *length)
373 {
374   JsonGeneratorPrivate *priv = generator->priv;
375   guint array_len = json_array_get_length (array);
376   guint i;
377   GString *buffer;
378   gboolean pretty = priv->pretty;
379   guint indent = priv->indent;
380
381   buffer = g_string_new ("");
382
383   if (pretty)
384     {
385       for (i = 0; i < (level * indent); i++)
386         g_string_append_c (buffer, priv->indent_char);
387     }
388
389   if (name)
390     {
391       if (pretty)
392         g_string_append_printf (buffer, "\"%s\" : ", name);
393       else
394         g_string_append_printf (buffer, "\"%s\":", name);
395     }
396
397   g_string_append_c (buffer, '[');
398
399   if (pretty)
400     g_string_append_c (buffer, '\n');
401
402   for (i = 0; i < array_len; i++)
403     {
404       JsonNode *cur = json_array_get_element (array, i);
405       guint sub_level = level + 1;
406       guint j;
407       gchar *value; 
408
409       switch (JSON_NODE_TYPE (cur))
410         {
411         case JSON_NODE_NULL:
412           if (pretty)
413             {
414               for (j = 0; j < (sub_level * indent); j++)
415                 g_string_append_c (buffer, priv->indent_char);
416             }
417           g_string_append (buffer, "null");
418           break;
419
420         case JSON_NODE_VALUE:
421           value = dump_value (generator, sub_level, NULL, cur, NULL);
422           g_string_append (buffer, value);
423           g_free (value);
424           break;
425
426         case JSON_NODE_ARRAY:
427           value = dump_array (generator, sub_level, NULL, json_node_get_array (cur), NULL);
428           g_string_append (buffer, value);
429           g_free (value);
430           break;
431
432         case JSON_NODE_OBJECT:
433           value = dump_object (generator, sub_level, NULL, json_node_get_object (cur), NULL);
434           g_string_append (buffer, value);
435           g_free (value);
436           break;
437         }
438
439       if ((i + 1) != array_len)
440         g_string_append_c (buffer, ',');
441
442       if (pretty)
443         g_string_append_c (buffer, '\n');
444     }
445
446   if (pretty)
447     {
448       for (i = 0; i < (level * indent); i++)
449         g_string_append_c (buffer, priv->indent_char);
450     }
451
452   g_string_append_c (buffer, ']');
453
454   if (length)
455     *length = buffer->len;
456
457   return g_string_free (buffer, FALSE);
458 }
459
460 static gchar *
461 dump_object (JsonGenerator *generator,
462              gint           level,
463              const gchar   *name,
464              JsonObject    *object,
465              gsize         *length)
466 {
467   JsonGeneratorPrivate *priv = generator->priv;
468   GList *members, *l;
469   GString *buffer;
470   gboolean pretty = priv->pretty;
471   guint indent = priv->indent;
472   guint i;
473
474   buffer = g_string_new ("");
475
476   if (pretty)
477     {
478       for (i = 0; i < (level * indent); i++)
479         g_string_append_c (buffer, priv->indent_char);
480     }
481
482   if (name)
483     {
484       if (pretty)
485         g_string_append_printf (buffer, "\"%s\" : ", name);
486       else
487         g_string_append_printf (buffer, "\"%s\":", name);
488     }
489
490   g_string_append_c (buffer, '{');
491
492   if (pretty)
493     g_string_append_c (buffer, '\n');
494
495   members = json_object_get_members (object);
496
497   for (l = members; l != NULL; l = l->next)
498     {
499       const gchar *member_name = l->data;
500       gchar *escaped_name = json_strescape (member_name);
501       JsonNode *cur = json_object_get_member (object, member_name);
502       guint sub_level = level + 1;
503       guint j;
504       gchar *value;
505
506       switch (JSON_NODE_TYPE (cur))
507         {
508         case JSON_NODE_NULL:
509           if (pretty)
510             {
511               for (j = 0; j < (sub_level * indent); j++)
512                 g_string_append_c (buffer, priv->indent_char);
513               g_string_append_printf (buffer, "\"%s\" : null", escaped_name);
514             }
515           else
516             {
517               g_string_append_printf (buffer, "\"%s\":null", escaped_name);
518             }
519           break;
520
521         case JSON_NODE_VALUE:
522           value = dump_value (generator, sub_level, escaped_name, cur, NULL);
523           g_string_append (buffer, value);
524           g_free (value);
525           break;
526
527         case JSON_NODE_ARRAY:
528           value = dump_array (generator, sub_level, escaped_name,
529                               json_node_get_array (cur), NULL);
530           g_string_append (buffer, value);
531           g_free (value);
532           break;
533
534         case JSON_NODE_OBJECT:
535           value = dump_object (generator, sub_level, escaped_name,
536                                json_node_get_object (cur), NULL);
537           g_string_append (buffer, value);
538           g_free (value);
539           break;
540         }
541
542       if (l->next != NULL)
543         g_string_append_c (buffer, ',');
544
545       if (pretty)
546         g_string_append_c (buffer, '\n');
547
548       g_free (escaped_name);
549     }
550
551   g_list_free (members);
552
553   if (pretty)
554     {
555       for (i = 0; i < (level * indent); i++)
556         g_string_append_c (buffer, priv->indent_char);
557     }
558
559   g_string_append_c (buffer, '}');
560
561   if (length)
562     *length = buffer->len;
563
564   return g_string_free (buffer, FALSE);
565 }
566
567 /**
568  * json_generator_new:
569  * 
570  * Creates a new #JsonGenerator. You can use this object to generate a
571  * JSON data stream starting from a data object model composed by
572  * #JsonNodes.
573  *
574  * Return value: the newly created #JsonGenerator instance
575  */
576 JsonGenerator *
577 json_generator_new (void)
578 {
579   return g_object_new (JSON_TYPE_GENERATOR, NULL);
580 }
581
582 /**
583  * json_generator_to_data:
584  * @generator: a #JsonGenerator
585  * @length: (out): return location for the length of the returned
586  *   buffer, or %NULL
587  *
588  * Generates a JSON data stream from @generator and returns it as a
589  * buffer.
590  *
591  * Return value: a newly allocated buffer holding a JSON data stream.
592  *   Use g_free() to free the allocated resources.
593  */
594 gchar *
595 json_generator_to_data (JsonGenerator *generator,
596                         gsize         *length)
597 {
598   JsonNode *root;
599   gchar *retval = NULL;
600
601   g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL);
602
603   root = generator->priv->root;
604   if (!root)
605     {
606       if (length)
607         *length = 0;
608
609       return NULL;
610     }
611
612   switch (JSON_NODE_TYPE (root))
613     {
614     case JSON_NODE_ARRAY:
615       retval = dump_array (generator, 0, NULL, json_node_get_array (root), length);
616       break;
617
618     case JSON_NODE_OBJECT:
619       retval = dump_object (generator, 0, NULL, json_node_get_object (root), length);
620       break;
621
622     case JSON_NODE_NULL:
623       retval = g_strdup ("null");
624       if (length)
625         *length = 4;
626       break;
627
628     case JSON_NODE_VALUE:
629       retval = dump_value (generator, 0, NULL, root, length);
630       break;
631     }
632
633   return retval;
634 }
635
636 /**
637  * json_generator_to_file:
638  * @generator: a #JsonGenerator
639  * @filename: path to the target file
640  * @error: return location for a #GError, or %NULL
641  *
642  * Creates a JSON data stream and puts it inside @filename, overwriting the
643  * current file contents. This operation is atomic.
644  *
645  * Return value: %TRUE if saving was successful.
646  */
647 gboolean
648 json_generator_to_file (JsonGenerator  *generator,
649                         const gchar    *filename,
650                         GError        **error)
651 {
652   gchar *buffer;
653   gsize len;
654   gboolean retval;
655
656   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
657   g_return_val_if_fail (filename != NULL, FALSE);
658
659   buffer = json_generator_to_data (generator, &len);
660   retval = g_file_set_contents (filename, buffer, len, error);
661   g_free (buffer);
662
663   return retval;
664 }
665
666 /**
667  * json_generator_to_stream:
668  * @generator: a #JsonGenerator
669  * @stream: a #GOutputStream
670  * @cancellable: (allow-none): a #GCancellable, or %NULL
671  * @error: return location for a #GError, or %NULL
672  *
673  * Outputs JSON data and streams it (synchronously) to @stream.
674  *
675  * Return value: %TRUE if the write operation was successful, and %FALSE
676  *   on failure. In case of error, the #GError will be filled accordingly
677  *
678  * Since: 0.12
679  */
680 gboolean
681 json_generator_to_stream (JsonGenerator  *generator,
682                           GOutputStream  *stream,
683                           GCancellable   *cancellable,
684                           GError        **error)
685 {
686   gboolean retval;
687   gchar *buffer;
688   gsize len;
689
690   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
691   g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
692
693   if (g_cancellable_set_error_if_cancelled (cancellable, error))
694     return FALSE;
695
696   buffer = json_generator_to_data (generator, &len);
697   retval = g_output_stream_write (stream, buffer, len, cancellable, error);
698   g_free (buffer);
699
700   return retval;
701 }
702
703 /**
704  * json_generator_set_root:
705  * @generator: a #JsonGenerator
706  * @node: a #JsonNode
707  *
708  * Sets @node as the root of the JSON data stream to be serialized by
709  * the #JsonGenerator.
710  *
711  * The passed @node is copied by the generator object, so it can be
712  * safely freed after calling this function.
713  */
714 void
715 json_generator_set_root (JsonGenerator *generator,
716                          JsonNode      *node)
717 {
718   g_return_if_fail (JSON_IS_GENERATOR (generator));
719
720   if (generator->priv->root == node)
721     return;
722
723   if (generator->priv->root != NULL)
724     {
725       json_node_free (generator->priv->root);
726       generator->priv->root = NULL;
727     }
728
729   if (node != NULL)
730     generator->priv->root = json_node_copy (node);
731
732   g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_ROOT]);
733 }
734
735 /**
736  * json_generator_get_root:
737  * @generator: a #JsonGenerator
738  *
739  * Retrieves a pointer to the root #JsonNode set using
740  * json_generator_set_root().
741  *
742  * Return value: (transfer none): a #JsonNode, or %NULL. The returned node
743  *   is owned by the #JsonGenerator and it should not be freed
744  *
745  * Since: 0.14
746  */
747 JsonNode *
748 json_generator_get_root (JsonGenerator *generator)
749 {
750   g_return_val_if_fail (JSON_IS_GENERATOR (generator), NULL);
751
752   return generator->priv->root;
753 }
754
755 /**
756  * json_generator_set_pretty:
757  * @generator: a #JsonGenerator
758  * @is_pretty: whether the generated string should be pretty printed
759  *
760  * Sets whether the generated JSON should be pretty printed, using the
761  * indentation character specified in the #JsonGenerator:indent-char
762  * property and the spacing specified in #JsonGenerator:indent property.
763  *
764  * Since: 0.14
765  */
766 void
767 json_generator_set_pretty (JsonGenerator *generator,
768                            gboolean       is_pretty)
769 {
770   JsonGeneratorPrivate *priv;
771
772   g_return_if_fail (JSON_IS_GENERATOR (generator));
773
774   priv = generator->priv;
775
776   is_pretty = !!is_pretty;
777
778   if (priv->pretty != is_pretty)
779     {
780       priv->pretty = is_pretty;
781
782       g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_PRETTY]);
783     }
784 }
785
786 /**
787  * json_generator_get_pretty:
788  * @generator: a #JsonGenerator
789  *
790  * Retrieves the value set using json_generator_set_pretty().
791  *
792  * Return value: %TRUE if the generated JSON should be pretty-printed, and
793  *   %FALSE otherwise
794  *
795  * Since: 0.14
796  */
797 gboolean
798 json_generator_get_pretty (JsonGenerator *generator)
799 {
800   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
801
802   return generator->priv->pretty;
803 }
804
805 /**
806  * json_generator_set_indent:
807  * @generator: a #JsonGenerator
808  * @indent_level: the number of repetitions of the indentation character
809  *   that should be applied when pretty printing
810  *
811  * Sets the number of repetitions for each indentation level.
812  *
813  * Since: 0.14
814  */
815 void
816 json_generator_set_indent (JsonGenerator *generator,
817                            guint          indent_level)
818 {
819   JsonGeneratorPrivate *priv;
820
821   g_return_if_fail (JSON_IS_GENERATOR (generator));
822
823   priv = generator->priv;
824
825   if (priv->indent != indent_level)
826     {
827       priv->indent = indent_level;
828
829       g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_INDENT]);
830     }
831 }
832
833 /**
834  * json_generator_get_indent:
835  * @generator: a #JsonGenerator
836  *
837  * Retrieves the value set using json_generator_set_indent().
838  *
839  * Return value: the number of repetitions per indentation level
840  *
841  * Since: 0.14
842  */
843 guint
844 json_generator_get_indent (JsonGenerator *generator)
845 {
846   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
847
848   return generator->priv->indent;
849 }
850
851 /**
852  * json_generator_set_indent_char:
853  * @generator: a #JsonGenerator
854  * @indent_char: a Unicode character to be used when indenting
855  *
856  * Sets the character to be used when indenting
857  *
858  * Since: 0.14
859  */
860 void
861 json_generator_set_indent_char (JsonGenerator *generator,
862                                 gunichar       indent_char)
863 {
864   JsonGeneratorPrivate *priv;
865
866   g_return_if_fail (JSON_IS_GENERATOR (generator));
867
868   priv = generator->priv;
869
870   if (priv->indent_char != indent_char)
871     {
872       priv->indent_char = indent_char;
873
874       g_object_notify_by_pspec (G_OBJECT (generator), generator_props[PROP_INDENT_CHAR]);
875     }
876 }
877
878 /**
879  * json_generator_get_indent_char:
880  * @generator: a #JsonGenerator
881  *
882  * Retrieves the value set using json_generator_set_indent_char().
883  *
884  * Return value: the character to be used when indenting
885  *
886  * Since: 0.14
887  */
888 gunichar
889 json_generator_get_indent_char (JsonGenerator *generator)
890 {
891   g_return_val_if_fail (JSON_IS_GENERATOR (generator), FALSE);
892
893   return generator->priv->indent_char;
894 }