DataTable в JSON - PullRequest
       28

DataTable в JSON

19 голосов
/ 16 января 2009

Мне недавно нужно было сериализовать данные в JSON. Где я сейчас нахожусь, мы все еще на .Net 2.0, поэтому я не могу использовать сериализатор JSON в .Net 3.5. Я подумал, что это должно было быть сделано раньше, поэтому я пошел смотреть онлайн и нашел число из различных параметров . Некоторые из них зависят от дополнительной библиотеки, которую мне было бы трудно протолкнуть здесь. Другие требуют сначала преобразовать в List<Dictionary<>>, что казалось немного неловким и ненужным. Другой рассматривал все значения как строку. По той или иной причине я не смог отстать ни от одного из них, поэтому я решил свернуть свой, что опубликовано ниже.

Как видно из прочтения комментариев //TODO, в некоторых местах он неполон. Этот код уже работает здесь, поэтому он работает в основном смысле. Места, где он неполный, - это места, где мы знаем, что наши производственные данные в настоящее время не будут попадать в него (нет временных интервалов или байтовых массивов в БД). Причина, по которой я публикую здесь, в том, что я чувствую, что это может быть немного лучше, и я хотел бы помочь закончить и улучшить этот код. Любой вход приветствуется.

Обратите внимание, что эта возможность встроена в .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 Schema действительно решает проблему, позволяя «подтипить» строку как литерал даты, но это все еще находится в стадии разработки, и потребуется какое-то время, прежде чем будет достигнуто какое-либо существенное принятие.

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

Ответы [ 3 ]

5 голосов
/ 17 января 2009

Поможет ли вам убедить ваших боссов установить библиотеку, если это расширение AJAX от Microsoft для .NET 2.0 ?

В них включено System.Web.Script.Serialization.JavascriptSerializer , которое используется на шаге 4 последней ссылки в вашем сообщении.

1 голос
/ 22 января 2009

Эй, приятель, все это здесь, в блоге Рика Сериализация DataTable с использованием Json.NET . Он подробно объясняет, как этого добиться, используя Json.NET из Джеймса Ньютона Кинга .

1 голос
/ 16 января 2009

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

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

...