最近、データテーブルを JSON にシリアル化する必要がありました。私がいるところでは、まだ .Net 2.0 を使用しているため、.Net 3.5 で JSON シリアライザーを使用することはできません。これは以前に行われたにちがいないと考えたので、オンラインで調べてみると、さまざまなオプションが見つかりました。それらのいくつかは、追加のライブラリに依存しているため、ここで押し通すのは困難です。他のものは、最初に に変換する必要がありますが、これは少しぎこちなく、不必要に思えました。別の方法では、すべての値を文字列のように扱いました。なんらかの理由で、私はそれらのどれも本当に支持できなかったので、私は自分自身をロールバックすることに決めました。 List<Dictionary<>>
コメントを読んでわかるよう//TODO
に、いくつかの場所で不完全です。このコードはすでにここで運用されているため、基本的な意味で「機能」します。不完全な場所は、本番データが現在ヒットしないことがわかっている場所です (db にタイムスパンやバイト配列がありません)。私がここに投稿している理由は、これがもう少し良くなる可能性があると感じているからです。このコードの完成と改善を手伝ってほしいのです。任意の入力を歓迎します。
この機能は .Net 3.5 以降に組み込まれているため、現在このコードを使用する唯一の理由は、まだ .Net 2.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 を使用するだけです。