Newtonsoft.Json.JsonSerializationException(从'值'在System.Data.SqlTypes.SqlDouble上获取值时出错)序列化SqlGeography

时间:2017-08-23 20:13:58

标签: c# sql-server json geolocation json.net

我尝试使用Newtonsoft.Json版本' Newtonsoft.Json.10.0.3'将DataTable对象序列化为Json。在数据库SQL Server 2012中。

该表格中包含一个类型为' geography'的列,其中包含SqlGeography类型的实例。

用于生成json的代码:

    public string SerializeToJson()
    {

     var connstring1 ="Data Source=server1;Initial Catalog=database1;user=xxx;password=yyy";
        var sql = "SELECT  * FROM table_1 "; //table_1 has a column of type geography
        using (var c1 = new SqlConnection(connstring1))
        {
            c1.Open();
            var da = new SqlDataAdapter()
            {
                SelectCommand = new SqlCommand(sql, c1)
            };

            DataSet ds1 = new DataSet("table");
            da.Fill(ds1, "table");
            var dt = ds1.Tables[0];

            //serialize to Json

            try
            {
                var options = new JsonSerializerSettings
                {
                    Formatting = Formatting.None
                };
                //this line fire exception for geography type
                var json = JsonConvert.SerializeObject(dt, options);
                return json;
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex);
            }                
        }
    }

我已经安装了程序集' Microsoft.SqlServer.Types'来自sql 2012的功能包

我使用带有SqlGeography列的数据表创建了一个完整的C#程序(独立于sql server安装)来显示问题 Try it

我收到错误:

  

Newtonsoft.Json.JsonSerializationException:从'值'获取值时出错on' System.Data.SqlTypes.SqlDouble'。 --->

     

System.Data.SqlTypes.SqlNullValueException:数据为空。    无法在Null值上调用此方法或属性。     在System.Data.SqlTypes.SqlDouble.get_Value()     在GetValue(对象)     at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

我已达到https://github.com/JamesNK/Newtonsoft.Json/issues/993,但无法提供帮助。

解决问题的任何帮助。

修改

基于@dbc注释,我提供了用于生成json的完整源代码。

完整的错误消息是:

  

Newtonsoft.Json.JsonSerializationException:从'值'获取值时出错on' System.Data.SqlTypes.SqlDouble'。 ---> > System.Data.SqlTypes.SqlNullValueException:数据为空。无法在Null值上调用此方法或属性。      在System.Data.SqlTypes.SqlDouble.get_Value()      在GetValue(对象)      at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

     

---内部异常堆栈跟踪结束---     at Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

     

at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer,Object value,JsonContainerContract contract,JsonProperty member,JsonProperty property,JsonContract& memberContract,Object& memberValue)     在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject   (JsonWriter编写器,Object值,JsonObjectContract契约,JsonProperty成员,JsonContainerContract collectionContract,JsonProperty containerProperty)     在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(   JsonWriter编写器,Object值,JsonContract valueContract,JsonProperty成员   ,JsonContainerContract containerContract,JsonProperty containerProperty)     在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject   (JsonWriter编写器,Object值,JsonObjectContract契约,JsonProperty成员,JsonContainerContract collectionContract,JsonProperty containerProperty)     在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(   JsonWriter编写器,Object值,JsonContract valueContract,JsonProperty成员   ,JsonContainerContract containerContract,JsonProperty containerProperty)     at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter,Object value,Type objectType)     at Newtonsoft.Json.Serialization.JsonSerializerProxy.SerializeInternal(JsonWriter jsonWriter,Object value,Type rootType)      在Newtonsoft.Json.Converters.DataTableConverter.WriteJson(JsonWriter writer,    对象值,JsonSerializer序列化器)     在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeConver   table(JsonWriter writer,JsonConverter converter,Object value,JsonContract contract,JsonContainerContract collectionContract,JsonProperty containerProperty)

     

at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(   JsonWriter编写器,Object值,JsonContract valueContract,JsonProperty成员   ,JsonContainerContract containerContract,JsonProperty containerProperty)     at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter,Object value,Type objectType)      at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter,Object value,Type objectType)      at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value,Type type,JsonSerializer jsonSerializer)     在Newtonsoft.Json.JsonConvert.SerializeObject(对象值,JsonSerializerSettings设置)      在F:\ JsonTester.cs的JsonTester.SerializeToJson()中:第104行

EDIT2:

我按照@dbc的描述启用跟踪,并获取以下日志:

  

2017-08-24T19:45:31.941 Info已开始使用转换器Newtonsoft.Json.Converters.DataTableConverter序列化System.Data.DataTable。路径'。

     

2017-08-24T19:45:31.972 Info已开始序列化Microsoft.SqlServer.Types.SqlGeography。路径' [0] .f1'。

     

2017-08-24T19:45:32.003 Info已开始序列化System.Data.SqlTypes.SqlInt32.Path' [0] .f1.STSrid'。

     

2017-08-24T19:45:32.003 Info完成序列化System.Data.SqlTypes.SqlInt32。路径' [0] .f1.STSrid'。

     

2017-08-24T19:45:32.003 Info已开始序列化System.Data.SqlTypes.SqlDouble。路径' [0] .f1.Lat'。

     

2017-08-24T19:45:32.003 Info完成序列化System.Data.SqlTypes.SqlDouble。路径' [0] .f1.Lat'。

     

2017-08-24T19:45:32.003 Info已开始序列化System.Data.SqlTypes.SqlDouble。路径' [0] .f1.Long'。

     

2017-08-24T19:45:32.003 Info完成序列化System.Data.SqlTypes.SqlDouble。路径' [0] .f1.Long'。

     

2017-08-24T19:45:32.003 Info已开始序列化System.Data.SqlTypes.SqlDouble。路径' [0] .f1.Z'。

     

2017-08-24T19:45:32.003错误序列化System.Data.SqlTypes.SqlDouble.Error获取值来自' Value' on' System.Data.SqlTypes.SqlDouble'。

     

2017-08-24T19:45:32.003错误序列化System.Data.DataTable时出错。从' Value'中获取价值时出错on' System.Data.SqlTypes.SqlDouble'。

2 个答案:

答案 0 :(得分:12)

看起来好像来自SqlDouble的{​​{1}}这样的原始类型无法通过Json.NET开箱即用,因为它们没有实现自己的System.Data.SqlTypes。来自docs

  

原始类型

     

.Net:TypeConverter(可兑换成字符串)
  JSON:String

有必要实现custom JsonConverter来序列化这些类型。对于内置的.Net类型,Json.NET有几个内置的converters,例如KeyValuePairConverter,所以这并不罕见。

TypeConverterSqlBooleanSqlBinary等不共享公共基类或除INullable之外的接口这一事实需要一些重复的代码:< / p>

SqlDouble

.Net fiddle分发工作own

如果需要反序列化由此创建的JSON,则还有两个问题。首先,SqlGeography的某些属性(例如LatLong)只是get-only。您需要创建自定义public static class SqlPrimitiveConverters { public static JsonSerializerSettings AddSqlConverters(this JsonSerializerSettings settings) { foreach (var converter in converters) settings.Converters.Add(converter); return settings; } static readonly JsonConverter[] converters = new JsonConverter[] { new SqlBinaryConverter(), new SqlBooleanConverter(), new SqlByteConverter(), new SqlDateTimeConverter(), new SqlDecimalConverter(), new SqlDoubleConverter(), new SqlGuidConverter(), new SqlInt16Converter(), new SqlInt32Converter(), new SqlInt64Converter(), new SqlMoneyConverter(), new SqlSingleConverter(), new SqlStringConverter(), // TODO: converters for primitives from System.Data.SqlTypes that are classes not structs: // SqlBytes, SqlChars, SqlXml // Maybe SqlFileStream }; } abstract class SqlPrimitiveConverterBase<T> : JsonConverter where T : struct, INullable, IComparable { protected abstract object GetValue(T sqlValue); public override bool CanConvert(Type objectType) { return typeof(T) == objectType; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { T sqlValue = (T)value; if (sqlValue.IsNull) writer.WriteNull(); else { serializer.Serialize(writer, GetValue(sqlValue)); } } } class SqlBinaryConverter : SqlPrimitiveConverterBase<SqlBinary> { protected override object GetValue(SqlBinary sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlBinary.Null; return (SqlBinary)serializer.Deserialize<byte[]>(reader); } } class SqlBooleanConverter : SqlPrimitiveConverterBase<SqlBoolean> { protected override object GetValue(SqlBoolean sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlBoolean.Null; return (SqlBoolean)serializer.Deserialize<bool>(reader); } } class SqlByteConverter : SqlPrimitiveConverterBase<SqlByte> { protected override object GetValue(SqlByte sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlByte.Null; return (SqlByte)serializer.Deserialize<byte>(reader); } } class SqlDateTimeConverter : SqlPrimitiveConverterBase<SqlDateTime> { protected override object GetValue(SqlDateTime sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlDateTime.Null; return (SqlDateTime)serializer.Deserialize<DateTime>(reader); } } class SqlDecimalConverter : SqlPrimitiveConverterBase<SqlDecimal> { protected override object GetValue(SqlDecimal sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlDecimal.Null; return (SqlDecimal)serializer.Deserialize<decimal>(reader); } } class SqlDoubleConverter : SqlPrimitiveConverterBase<SqlDouble> { protected override object GetValue(SqlDouble sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlDouble.Null; return (SqlDouble)serializer.Deserialize<double>(reader); } } class SqlGuidConverter : SqlPrimitiveConverterBase<SqlGuid> { protected override object GetValue(SqlGuid sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlGuid.Null; return (SqlGuid)serializer.Deserialize<Guid>(reader); } } class SqlInt16Converter : SqlPrimitiveConverterBase<SqlInt16> { protected override object GetValue(SqlInt16 sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlInt16.Null; return (SqlInt16)serializer.Deserialize<short>(reader); } } class SqlInt32Converter : SqlPrimitiveConverterBase<SqlInt32> { protected override object GetValue(SqlInt32 sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlInt32.Null; return (SqlInt32)serializer.Deserialize<int>(reader); } } class SqlInt64Converter : SqlPrimitiveConverterBase<SqlInt64> { protected override object GetValue(SqlInt64 sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlInt64.Null; return (SqlInt64)serializer.Deserialize<long>(reader); } } class SqlMoneyConverter : SqlPrimitiveConverterBase<SqlMoney> { protected override object GetValue(SqlMoney sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlMoney.Null; return (SqlMoney)serializer.Deserialize<decimal>(reader); } } class SqlSingleConverter : SqlPrimitiveConverterBase<SqlSingle> { protected override object GetValue(SqlSingle sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlSingle.Null; return (SqlSingle)serializer.Deserialize<float>(reader); } } class SqlStringConverter : SqlPrimitiveConverterBase<SqlString> { protected override object GetValue(SqlString sqlValue) { return sqlValue.Value; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return SqlString.Null; return (SqlString)serializer.Deserialize<string>(reader); } } 才能完全反序列化此类型。

其次,Json.NET无法将行值的复杂对象的JSON反序列化为无类型JsonConverter。因此,如果需要反序列化包含复杂对象的JSON(例如序列化的DataTable),则可以使用以下选项:

  1. 创建并反序列化为已键入的SqlGeography

  2. 直接使用DataTable填充预先存在的DataTableConverter预分配列,如here所示。

  3. 反序列化为DTOs列表,如下所示:

    DataTable

    根据需要public class TableRowDTO { [JsonConverter(typeof(SqlGeographyConverter))] public SqlGeography f1 { get; set; } public int id { get; set; } } SqlGeographyConverter的自定义JsonConverter

    然后做:

    SqlGeography

答案 1 :(得分:0)

实现Json转换器当然是处理无法在Json.Net中序列化SqlGeography的一种方法,但是我认为一个更简单的解决方案是不直接序列化SqlGeography。而是创建一个自定义类,其中包含SqlGeography的所有数据,然后对它进行序列化/反序列化。

public class SqlGeographyResult
    {
        private byte[] bytes { get; }

        public SqlGeographyResult(SqlGeography geography)
        {
            bytes = geography.Serialize().Value;
        }

        public SqlGeography ToGeography()
        {
            return SqlGeography.Deserialize(new System.Data.SqlTypes.SqlBytes(bytes));
        }
    }