【问题标题】:Serialize DataSet with current version of System.Text.Json.JsonSerializer使用当前版本的 System.Text.Json.JsonSerializer 序列化 DataSet
【发布时间】:2020-05-03 22:24:42
【问题描述】:

您对我们如何使用 System.Text.Json.JsonSerializer序列化 DataSet、DataTable 有什么建议吗?
目前它抛出这个异常: '检测到不支持的可能对象循环。这可能是由于循环或对象深度大于最大允许深度 64 造成的。'

【问题讨论】:

    标签: asp.net-core serialization system.text.json


    【解决方案1】:

    目前在System.Text.Json 中没有对DataSetDataTable 等类型的内置支持(从.NET Core 3.1 开始)。为了能够序列化这些类型,您需要为您需要的类型实现自己的JsonConverter<T>,并在JsonSerializerOptions 中注册它。为您要求的特定类型编写序列化序列应该相当容易。

    这是一个适用于序列化的示例(省略了反序列化组件):

    public class DataTableConverter : JsonConverter<DataTable>
    {
        public override DataTable Read(ref Utf8JsonReader reader, Type typeToConvert,
            JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    
        public override void Write(Utf8JsonWriter writer, DataTable value,
            JsonSerializerOptions options)
        {
            writer.WriteStartArray();
    
            foreach (DataRow row in value.Rows)
            {
                writer.WriteStartObject();
                foreach (DataColumn column in row.Table.Columns)
                {
                    object columnValue = row[column];
    
                    // If necessary:
                    if (options.IgnoreNullValues)
                    {
                        // Do null checks on the values here and skip writing.
                    }
    
                    writer.WritePropertyName(column.ColumnName);
                    JsonSerializer.Serialize(writer, columnValue, options);
                }
                writer.WriteEndObject();
            }
    
            writer.WriteEndArray();
        }
    }
    
    public class DataSetConverter : JsonConverter<DataSet>
    {
        public override DataSet Read(ref Utf8JsonReader reader, Type typeToConvert,
            JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    
        public override void Write(Utf8JsonWriter writer, DataSet value,
            JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            foreach (DataTable table in value.Tables)
            {
                writer.WritePropertyName(table.TableName);
                JsonSerializer.Serialize(writer, table, options);
            }
            writer.WriteEndObject();
        }
    }
    
    private static void DataSet_Serialization_WithSystemTextJson()
    {
        var options = new JsonSerializerOptions()
        {
            Converters = { new DataTableConverter(), new DataSetConverter() }
        };
    
        (DataTable table, DataSet dataSet) = GetDataSetAndTable();
    
        string jsonDataTable = JsonSerializer.Serialize(table, options);
        // [{"id":0,"item":"item 0"},{"id":1,"item":"item 1"}]
        Console.WriteLine(jsonDataTable);
    
        string jsonDataSet = JsonSerializer.Serialize(dataSet, options);
        // {"Table1":[{"id":0,"item":"item 0"},{"id":1,"item":"item 1"}]}
        Console.WriteLine(jsonDataSet);
    
        // Local function to create a sample DataTable and DataSet
        (DataTable, DataSet) GetDataSetAndTable()
        {
            dataSet = new DataSet("dataSet");
    
            table = new DataTable();
            DataColumn idColumn = new DataColumn("id", typeof(int))
            {
                AutoIncrement = true
            };
    
            DataColumn itemColumn = new DataColumn("item");
    
            table.Columns.Add(idColumn);
            table.Columns.Add(itemColumn);
    
            dataSet.Tables.Add(table);
    
            for (int i = 0; i < 2; i++)
            {
                DataRow newRow = table.NewRow();
                newRow["item"] = "item " + i;
                table.Rows.Add(newRow);
            }
    
            dataSet.AcceptChanges();
    
            return (table, dataSet);
        }
    }
    

    本文档可能会提供更多指导: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to

    【讨论】:

    • 这个答案包含了很好的线索,但仍然没有解决问题。顺便谢谢你的回答。
    • 您能否详细说明您要查找的内容,以便我更新答案?需要什么类型的DataSet/DataTables序列化成JSON,为什么目前的方案不行?
    • 在编写数据集的一部分时,您直接调用序列化程序,而您应该将数据表发送到代码中已经存在的下一个类。此外你没有任何反序列化的东西。
    【解决方案2】:

    目前不支持使用 System.Text.Json 处理引用循环。解决方法:

    .Net Core 3.0 possible object cycle was detected which is not supported

    https://www.thecodebuzz.com/jsonexception-possible-object-cycle-detected-object-depth/

    您可以在违规属性上添加自定义转换器来处理引用自身的对象,或者只是将 JsonIgnore 属性添加到违规属性。

    【讨论】:

      【解决方案3】:

      这是 DataTable 和 DataSet 自定义转换器的代码。它仅支持常见/简单类型,这是降低面临序列化器/反序列化器漏洞风险的好方法。

      namespace MyJsonConverters
      {
          public class DataTableJsonConverter : JsonConverter<DataTable>
          {
              public override DataTable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
              {
                  DataTable dt = new DataTable();
                  while (reader.Read())
                  {
                      if (reader.TokenType == JsonTokenType.EndObject)
                          return dt;
      
                      if (reader.TokenType != JsonTokenType.PropertyName)
                          throw new JsonException();
      
                      string propertyName = reader.GetString();
                      if (propertyName == "TableName")
                      {
                          dt.TableName = JsonSerializer.Deserialize<string>(ref reader, options);
                      }
                      else if (propertyName == "Columns")
                      {
                          JsonDataColumn[] jsonDataColumns = JsonSerializer.Deserialize<JsonDataColumn[]>(ref reader, options);
                          foreach (JsonDataColumn jsonDataColumn in jsonDataColumns)
                          {
                              Type dataType = Type.GetType(jsonDataColumn.DataType);
                              dt.Columns.Add(jsonDataColumn.ColumnName, dataType);
                              if (jsonDataColumn.MaxLength > 0)
                                  dt.Columns[dt.Columns.Count].MaxLength = jsonDataColumn.MaxLength;
                          }
                      }
                      else if (propertyName == "Rows")
                      {
                          JsonElement[][] jsonRows = JsonSerializer.Deserialize<JsonElement[][]>(ref reader, options);
                          foreach (JsonElement[] jsonElements in jsonRows)
                          {
                              DataRow drNew = dt.NewRow();
                              int i = -1;
                              foreach(DataColumn dc in dt.Columns)
                              {
                                  i++;
      
                                  if (jsonElements[i].ToString() == "{}") // DBNull
                                      continue;
                                  else if (dc.DataType == typeof(string))
                                      drNew[dc] = jsonElements[i].GetString();
                                  else if (dc.DataType == typeof(bool))
                                      drNew[dc] = jsonElements[i].GetBoolean();
                                  else if (dc.DataType == typeof(DateTime))
                                      drNew[dc] = jsonElements[i].GetDateTime();
                                  else if (dc.DataType == typeof(Int64))
                                      drNew[dc] = jsonElements[i].GetInt64();
                                  else if (dc.DataType == typeof(Int32))
                                      drNew[dc] = jsonElements[i].GetInt32();
                                  else if (dc.DataType == typeof(Int16))
                                      drNew[dc] = jsonElements[i].GetInt16();
                                  else if (dc.DataType == typeof(UInt64))
                                      drNew[dc] = jsonElements[i].GetUInt64();
                                  else if (dc.DataType == typeof(UInt32))
                                      drNew[dc] = jsonElements[i].GetUInt32();
                                  else if (dc.DataType == typeof(UInt16))
                                      drNew[dc] = jsonElements[i].GetUInt16();
                                  else if (dc.DataType == typeof(byte))
                                      drNew[dc] = jsonElements[i].GetByte();
                                  else if (dc.DataType == typeof(sbyte))
                                      drNew[dc] = jsonElements[i].GetSByte();
                                  else if (dc.DataType == typeof(Single))
                                      drNew[dc] = jsonElements[i].GetSingle();
                                  else if (dc.DataType == typeof(Double))
                                      drNew[dc] = jsonElements[i].GetDouble();
                                  else if (dc.DataType == typeof(Decimal))
                                      drNew[dc] = jsonElements[i].GetDecimal();
                                  else if (dc.DataType == typeof(Guid))
                                      drNew[dc] = jsonElements[i].GetGuid();
                                  else if (dc.DataType == typeof(byte[]))
                                      drNew[dc] = jsonElements[i].GetBytesFromBase64();
                                  else
                                      throw new JsonException("Column data type not supported in DataTableJsonConverter: " + dc.DataType);
                              }
                              dt.Rows.Add(drNew);
                          }
                      }
                  }
                  throw new JsonException();
              }
      
              public override void Write(Utf8JsonWriter writer, DataTable dt, JsonSerializerOptions options)
              {
                  JsonDataTable j = new JsonDataTable
                  {
                      TableName = dt.TableName,
                      Columns = dt.Columns.Cast<DataColumn>().Select(c => new JsonDataColumn { ColumnName = c.ColumnName, DataType = c.DataType.ToString(), MaxLength = c.MaxLength }).ToArray(),
                      Rows = dt.Rows.Cast<DataRow>().Select(dr => dr.ItemArray).ToArray()
                  };
                  JsonSerializer.Serialize(writer, j, options);
              }
      
      
              public class JsonDataTable
              {
                  public string TableName { get; set; }
                  public JsonDataColumn[] Columns { get; set; }
                  public object[][] Rows { get; set; }
              }
      
              public class JsonDataColumn
              {
                  public string ColumnName { get; set; }
                  public string DataType { get; set; }
                  public int MaxLength { get; set; }
              }
          }
      
          public class DataSetJsonConverter : JsonConverter<DataSet>
          {
              public override DataSet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
              {
                  DataTableJsonConverter dataTableJsonConverter = new DataTableJsonConverter(); 
                  DataSet ds = new DataSet();
                  while (reader.Read())
                  {
                      if (reader.TokenType == JsonTokenType.EndObject)
                          return ds;
      
                      if (reader.TokenType != JsonTokenType.PropertyName)
                          throw new JsonException();
      
                      string propertyName = reader.GetString();
                      if (propertyName == "DataSetName")
                      {
                          ds.DataSetName = JsonSerializer.Deserialize<string>(ref reader, options);
                      }
                      else if (propertyName == "Tables")
                      {
                          reader.Read();
                          if (reader.TokenType != JsonTokenType.StartArray)
                              throw new JsonException();
                          while (reader.Read())
                          {
                              if (reader.TokenType == JsonTokenType.EndArray)
                                  break;
                              DataTable dt = dataTableJsonConverter.Read(ref reader, null, options);
                              ds.Tables.Add(dt);
                          }  
                      }
                  }
                  throw new JsonException();
              }
      
              public override void Write(Utf8JsonWriter writer, DataSet ds, JsonSerializerOptions options)
              {
                  DataTableJsonConverter dataTableJsonConverter = new DataTableJsonConverter();
      
                  writer.WriteStartObject();
                  writer.WritePropertyName("DataSetName");
                  writer.WriteStringValue(ds.DataSetName);
                  writer.WritePropertyName("Tables");
                  writer.WriteStartArray();
                  foreach(DataTable dt in ds.Tables)
                      dataTableJsonConverter.Write(writer, dt, options);
                  writer.WriteEndArray();
                  writer.WriteEndObject();
              }
          }
      }
      

      用法:

      public class MyClass
      {
          [System.Text.Json.Serialization.JsonConverter(typeof(MyJsonConverters.DataTableJsonConverter))] 
          public DataTable DataTable { get; set; }
          [System.Text.Json.Serialization.JsonConverter(typeof(MyJsonConverters.DataSetJsonConverter))]
          public DataSet DataSet { get; set; }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-03-11
        • 1970-01-01
        • 2013-08-30
        • 1970-01-01
        • 1970-01-01
        • 2013-05-24
        • 1970-01-01
        相关资源
        最近更新 更多