2015-09-22 4 views
1

Я пытаюсь преобразовать .NET DataTable в файл JSON и затем десериализовать файл JSON обратно в DataTable. Справедливости ради я подумал.deserialize datatable с отсутствующим первым столбцом

Однако у меня есть таблица, 3 строки по 3 столбца, каждый элемент имеет тип double. Если любое значение в первой строке равно NULL, когда JSON.Net десериализует json-файл в объект DataTable, все значения столбца, который был пустым в первой строке, становятся строками.

Чтобы быть ясным, это только если значение в первой строке равно null, что это происходит. Если любые значения равны нулю в любой другой строке, чем первая, остальные значения в этом столбце остаются удвоенными.

  1. Если я заменяю нуль двойным, все работает как ожидалось (однако я не могу это сделать в моем случае).

  2. Если установить NullValueHandling = NullValueHandling.Ignore, все значения оставались в парном разряде, за исключением первой строки в настоящее время получает перечисленные в последней строке:

Пример:

"Column2": 1.0, 
"Column3": 1.1 
}, 
{ 
    "Column1": 0.0, 
    "Column2": 0.5, 
    "Column3": 2.0 
}, 

становится:

"Column2": 1.0, 
    "Column3": 1.1 
}, 
{ 
    "Column2": 0.5, 
    "Column3": 2.0, 
    "Column1": 0.0 
}, 

Мне нужно, чтобы десериализовать JSON, держать колонны в порядке, nd не имеют нулевых значений в первой строке, поэтому все значения в этой строке становятся строками. Мне также нужно оставить столбец 1 первой строки (в приведенном выше случае) нулевым - не волнует, является ли она пустой строкой или DBNull.

Любые мысли? (Мой тестовый код below..comment/раскомментировать NullValueHandling увидеть проблему)

 DataTable table = new DataTable("MyTable"); 
     table.Columns.Add("Column1", typeof(double)); 
     table.Columns.Add("Column2", typeof(double)); 
     table.Columns.Add("Column3", typeof(double)); 

     for (int i = 0; i < 10; i++) { 
      if (i == 0) 
       table.Rows.Add(null, 1.0, 1.1); 
      else 
       table.Rows.Add(0.0, 0.5, 2.0); 
     } 

     JsonSerializer serializer = new JsonSerializer(); 
     //serializer.TypeNameHandling = TypeNameHandling.All; 
     serializer.NullValueHandling = NullValueHandling.Ignore; 
     using (StreamWriter sw1 = new StreamWriter("1st.json")) 
     using (JsonWriter writer1 = new JsonTextWriter(sw1)) 
     { 
      writer1.Formatting = Formatting.Indented; 
      serializer.Serialize(writer1, table); 
     } 

     DataTable newtable; 
     using (StreamReader sr = new StreamReader("1st.json")) 
     using (JsonReader reader = new JsonTextReader(sr)) 
     { 
      newtable = (DataTable)serializer.Deserialize(reader, typeof(DataTable)); 
     } 

     using (StreamWriter sw = new StreamWriter("3rd.json")) 
     using (JsonWriter writer = new JsonTextWriter(sw)) 
     { 
      writer.Formatting = Formatting.Indented; 
      serializer.Serialize(writer, newtable); 
     } 
+0

Я бы предложил вам создать класс и сериализовать/де-сериализовать в него вместо DataTable, если это возможно. – Jag

+0

Спасибо за предложение, но это невозможно в количестве времени, которое я должен преобразовать 100k строк кода проекта из XML в json, когда DataTable бросается на весь проект (это был ужасный дизайн, но вы не знаете, всегда выбирайте). Тем временем я буду экспериментировать с несколькими другими библиотеками json. Надеюсь, у кого-то есть идея. Я просто буду доволен тем, «почему» это происходит. Кстати ... код, приведенный выше, является неловко простым примером, который я написал, чтобы проверить, что это JSON.net, а не какое-то другое взаимодействие в кодовой базе. Массив или список 2-го уровня будет проще. – Dime

+0

Json.NET является открытым исходным кодом в соответствии с [лицензией MIT] (https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md), поэтому вы можете скопировать и изменить его код. Создайте версию ['DataTableConverter'] (https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DataTableConverter.cs), называемую, например,' DoubleDataTableConverter', в которой 'static Тип GetColumnDataType (читатель JsonReader)' возвращает 'typeof (double)' для нулевого значения. – dbc

ответ

1

Json.NET является открытым исходным кодом под MIT License, поэтому одна из возможностей было бы создать модифицированную версию своей DataTableConverter в соответствии с вашими потребностями , Его исходный код доступен here.

Сначала создайте свою собственную раздвоенную версию этого класса, называемую, скажем, JsonDefaultTypeDataTableConverter<T>. Измените метод GetColumnDataType вернуть typeof(T) для нулевого значения столбца:

/// <summary> 
/// Converts a <see cref="DataTable"/> to and from JSON. 
/// Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DataTableConverter.cs 
/// </summary> 
public class JsonDefaultTypeDataTableConverter<T> : JsonConverter 
{ 
    private static Type GetColumnDataType(JsonReader reader) 
    { 
     JsonToken tokenType = reader.TokenType; 

     switch (tokenType) 
     { 
      case JsonToken.Integer: 
      case JsonToken.Boolean: 
      case JsonToken.Float: 
      case JsonToken.String: 
      case JsonToken.Date: 
      case JsonToken.Bytes: 
       return reader.ValueType; 
      case JsonToken.Null: 
      case JsonToken.Undefined: 
       return typeof(T); // WAS typeof(string). 
      case JsonToken.StartArray: 
       CheckedRead(reader); 
       if (reader.TokenType == JsonToken.StartObject) 
       { 
        return typeof (DataTable); // nested datatable 
       } 

       Type arrayType = GetColumnDataType(reader); 
       return arrayType.MakeArrayType(); 
      default: 
       throw new JsonSerializationException(string.Format("Unexpected JSON token when reading DataTable: {0}", tokenType)); 
     } 
    } 
} 

Вам также нужно исправить звонки бросить JsonSerializationException на вокруг линии 232, например, следующим образом:

private static void CheckedRead(JsonReader reader) 
    { 
     if (!reader.Read()) 
     { 
      throw new JsonSerializationException(string.Format("Unexpected end when reading DataTable.")); 
     } 
    } 

И вокруг линии 114:

 if (reader.TokenType != JsonToken.StartArray) 
     { 
      throw new JsonSerializationException(string.Format("Unexpected JSON token when reading DataTable. Expected StartArray, got {0}.", reader.TokenType)); 
     } 

Теперь, учитывая, что вы знают ваша таблица содержит столбцы double значений, вы можете использовать его как это:

 JsonSerializer serializer = new JsonSerializer(); 
     //serializer.TypeNameHandling = TypeNameHandling.All; 
     //serializer.NullValueHandling = NullValueHandling.Ignore; -- DO NOT USE THIS OPTION. 
     serializer.Converters.Add(new JsonDefaultTypeDataTableConverter<double>()); 

Обратите внимание, что, делая это, вы не изменяя внутренности самого Json.NET, вы копируете и модифицируя один из своих стандартных стандартов converters для широко используемых типов .Net.

Обновление: полная версия here.

+0

Вот полная версия конвертера: http://pastebin.com/S79bacwe. Он основывается на VS 2008 или более поздней версии. Вам не нужно перестраивать все Json.NET, чтобы использовать его, просто добавьте этот файл в локальное решение. – dbc

+0

Большое спасибо! Это решило проблему. Знаете ли вы, почему он по-прежнему будет помещать Column1 в качестве последнего столбца, если я использую NullValueHandling.Ignore? Я прокомментировал это и получил ожидаемое поведение, но было бы неплохо использовать его для других частей сериализованного класса. – Dime

+0

@Dime - я * думаю ** это случается, потому что ** написанный ** json отсутствует столбец, который испортил порядок столбцов при чтении. Вы можете удалить 'if (serializer.NullValueHandling == NullValueHandling.Ignore && (columnValue == null || columnValue == DBNull.Value))' строка из 'JsonDefaultTypeDataTableConverter .WriteJson()' – dbc

Смежные вопросы