web-dev-qa-db-ja.com

DataTableからJSONへ

最近、データテーブルをJSONにシリアル化する必要がありました。私がいる場所ではまだ.Net2.0を使用しているため、.Net3.5でJSONシリアライザーを使用することはできません。これは以前に行われたに違いないと思ったので、オンラインで調べて found a number of differentoptions 。それらのいくつかは追加のライブラリに依存しているので、ここでプッシュするのは難しいでしょう。他の人は最初にList<Dictionary<>>に変換する必要がありますが、これは少し厄介で不必要に思えました。別の人はすべての値を文字列のように扱いました。どういうわけか、私はそれらのどれも本当に後れを取ることができなかったので、私は自分自身を転がすことに決めました。それは以下に掲載されています。

//TODOのコメントを読むとわかるように、いくつかの場所で不完全です。このコードはすでにここで本番環境にあるため、基本的な意味で「機能」します。不完全な場所は、本番データが現在ヒットしないことがわかっている場所です(データベースにタイムスパンやバイト配列はありません)。私がここに投稿する理由は、これがもう少し良くなる可能性があると感じているためです。このコードの仕上げと改善を支援したいと思います。どんな入力でも歓迎します。

この機能は.Net 3.5以降に組み込まれているため、今日このコードを使用する唯一の理由は、まだ.Net2.0に制限されている場合です。それでも、JSON.Netはこの種のgotoライブラリになっています。

public static class JSONHelper
{
    public static string FromDataTable(DataTable dt)
    {
        string rowDelimiter = "";

        StringBuilder result = new StringBuilder("[");
        foreach (DataRow row in dt.Rows)
        {
            result.Append(rowDelimiter);
            result.Append(FromDataRow(row));
            rowDelimiter = ",";
        }
        result.Append("]");

        return result.ToString();
    }

    public static string FromDataRow(DataRow row)
    {
        DataColumnCollection cols = row.Table.Columns;
        string colDelimiter = "";

        StringBuilder result = new StringBuilder("{");       
        for (int i = 0; i < cols.Count; i++)
        { // use index rather than foreach, so we can use the index for both the row and cols collection
            result.Append(colDelimiter).Append("\"")
                  .Append(cols[i].ColumnName).Append("\":")
                  .Append(JSONValueFromDataRowObject(row[i], cols[i].DataType));

            colDelimiter = ",";
        }
        result.Append("}");
        return result.ToString();
    }

    // possible types:
    // http://msdn.Microsoft.com/en-us/library/system.data.datacolumn.datatype(VS.80).aspx
    private static Type[] numeric = new Type[] {typeof(byte), typeof(decimal), typeof(double), 
                                     typeof(Int16), typeof(Int32), typeof(SByte), typeof(Single),
                                     typeof(UInt16), typeof(UInt32), typeof(UInt64)};

    // I don't want to rebuild this value for every date cell in the table
    private static long EpochTicks = new DateTime(1970, 1, 1).Ticks;

    private static string JSONValueFromDataRowObject(object value, Type DataType)
    {
        // null
        if (value == DBNull.Value) return "null";

        // numeric
        if (Array.IndexOf(numeric, DataType) > -1)
            return value.ToString(); // TODO: eventually want to use a stricter format. Specifically: separate integral types from floating types and use the "R" (round-trip) format specifier

        // boolean
        if (DataType == typeof(bool))
            return ((bool)value) ? "true" : "false";

        // date -- see http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx
        if (DataType == typeof(DateTime))       
            return "\"\\/Date(" + new TimeSpan(((DateTime)value).ToUniversalTime().Ticks - EpochTicks).TotalMilliseconds.ToString() + ")\\/\"";

        // TODO: add Timespan support
        // TODO: add Byte[] support

        //TODO: this would be _much_ faster with a state machine
        //TODO: way to select between double or single quote literal encoding
        //TODO: account for database strings that may have single \r or \n line breaks
        // string/char  
        return "\"" + value.ToString().Replace(@"\", @"\\").Replace(Environment.NewLine, @"\n").Replace("\"", @"\""") + "\"";
    }
}

更新:
これは今では古いですが、このコードが日付を処理する方法について何か指摘したいと思います。私が使用した形式は、URLの正確な根拠のために、当時は理にかなっています。ただし、その理論的根拠には次のものが含まれます。

正直なところ、JSONスキーマは文字列を日付リテラルとして「サブタイプ化」できるようにすることで問題を解決しますが、これはまだ進行中であり、重要な採用に達するまでには時間がかかります。

さて、時間が経ちました。今日では、 ISO 8601 日付形式を使用しても問題ありません。私はわざわざコードを変更するつもりはありません。なぜなら、これは本当に古いからです。 JSON.Netを使用してください。

19
Joel Coehoorn

それがMicrosoftの 。NET 2.0のAJAX拡張機能 の場合、上司にライブラリをインストールするように説得するのに役立ちますか?

それらに含まれているのは System.Web.Script.Serialization.JavascriptSerializer で、これは投稿の 最後のリンク のステップ4で使用されます。

5
Powerlord

やあ、バディ、それはすべてここリックのブログ投稿にあります Json.NETを使用してDataTableをシリアル化する 。彼は、 Json.NET from James Newton King を使用してそれを実現する方法を詳細に説明しています。

1
TheVillageIdiot

私はこれを見つけました: http://www.bramstein.com/projects/xsltjson/ データテーブルをxmlに変換し、xsltスタイルシートを使用してxmlをjsonに変換できます。

これは、実際の解決策というよりも回避策です。

1
tuinstoel