DataTable для JSON

Мне недавно понадобилось сериализовать данные для JSON. Где я нахожусь, мы все еще на .Net 2.0, поэтому я не могу использовать сериализатор JSON в .Net 3.5. Я полагал, что это, должно быть, было сделано раньше, поэтому я пошел искать онлайн и нашел a номер разные options. Некоторые из них зависят от дополнительной библиотеки, с которой мне было бы трудно пробиться сюда. Другие требуют сначала конвертировать в 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("\"", @"\""") + "\"";
    }
}

Update:
Это уже сейчас, но я хотел бы указать на то, как этот код обрабатывает даты. Формат, который я использовал, имел смысл в то время, для точного обоснования в URL-адресе. Однако это обоснование включает следующее:

Чтобы быть абсолютно честным, JSON Schema решает проблему, позволяя "подтипировать" строку как литерал даты, но это все еще продолжается, и потребуется некоторое время, прежде чем будет достигнуто значительное принятие.

Ну, прошло время. Сегодня можно просто использовать формат даты ISO 8601. Я не собираюсь менять код, потому что на самом деле: это древний. Просто используйте JSON.Net.

Ответ 2

Я нашел это: http://www.bramstein.com/projects/xsltjson/ Вы можете преобразовать свой datatable в xml и использовать таблицу стилей xslt для преобразования xml в json.

Это скорее обходное решение, чем реальное решение.