DataContractJsonSerializerのJSON出力を改行・インデントする

先日の記事の補足です。

mokake.hatenablog.com

DataContractJsonSerializerJSONを生成すると、改行なしに1行になってしまいます。機械処理上はいいのですが、やはり人が見る場合もあるので、そこは考えてほしいところ。

ちょっと調べてみたら、実は簡単にできるんですね。

stackoverflow.com

ここにコードがありました。

ポイントは、.NET4.5で追加された、JsonReaderWriterFactory.CreateJsonWriter(Stream, Encoding, Boolean, Boolean)メソッド(および、String引数がさらに追加されたもの)です。

CreateJsonWriterメソッド自体は以前からあったのですが、.NET4.5で引数にindentが加わりました。そこで、これを指定して生成したXmlDictionaryWriterを使えば、改行というかインデントされたJSONが得られます。

また、先ほどのSOの回答では、InvariantCultureを指定することで、カルチャの影響(例えば小数点がピリオドかカンマか)も除去でき、安定した動作が期待できます。

SOの回答では、ファイル入出力が前提でしたが、JSONを文字列として処理するように書き換えてみました。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Threading;
using System.Globalization;
using System.Diagnostics;

namespace JsonTestNET45
{
    public static class Json
    {
        private static readonly DataContractJsonSerializerSettings Settings = 
            new DataContractJsonSerializerSettings
            {
                UseSimpleDictionaryFormat = true
            };

        private static CultureInfo _cultureStorage = null;

        private static void PushCulture(CultureInfo culture)
        {
            if (_cultureStorage != null)
                throw new ApplicationException("PushCulture : double pushed!");
            _cultureStorage = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = culture;
        }

        private static void PopCulture()
        {
            if (_cultureStorage == null)
                throw new ApplicationException("PopCulture : double popped!");
            Thread.CurrentThread.CurrentCulture = _cultureStorage;
            _cultureStorage = null;
        }

        public static string ToJson<T>(this T data)
        {
            try
            {
                using (var stream = new MemoryStream())
                {
                    PushCulture(CultureInfo.InvariantCulture);

                    try
                    {
                        using (var writer =
                            JsonReaderWriterFactory.CreateJsonWriter(
                            stream, Encoding.UTF8, true, true, "  "))
                        {
                            var serializer = new DataContractJsonSerializer(typeof(T), Settings);
                            serializer.WriteObject(writer, data);
                            writer.Flush();
                            var json = Encoding.UTF8.GetString(stream.ToArray());
                            return json;
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("ObjectToJson Error (inner) : " + ex.ToString());
                        throw;
                    }
                    finally
                    {
                        PopCulture();
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("ObjectToJson Error (outer) : " + ex.ToString());
                throw;
            }
        }

        public static T JsonToObject<T>(this string json)
        {
            try
            {
                using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
                {
                    PushCulture(CultureInfo.InvariantCulture);

                    try
                    {
                        var serializer = new DataContractJsonSerializer(typeof(T), Settings);
                        var item = (T)serializer.ReadObject(stream);
                        return item;
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("OpenAsJson Error (inner) : " + ex.ToString());
                        throw;
                    }
                    finally
                    {
                        PopCulture();
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("OpenAsJson Error (outer) : " + ex.ToString());
                throw;
            }
        }
    }
}

なお、これらを使う場合に型を外部から入力してそのまま使うと脆弱性につながりますのでご注意くださいませ。