【问题标题】:Deserializing DbGeometry with Newtonsoft.Json使用 Newtonsoft.Json 反序列化 DbGeometry
【发布时间】:2021-06-22 21:58:24
【问题描述】:

我正在按照 John Papa 在他最新的 PluralSight 课程中概述的方法使用 Angular、Breeze 和 Web API 2 构建一个 SPA。

一切正常,我可以将信息拉回、更新、插入、删除回服务器。但是,我使用的是空间类型,当我尝试使用空间类型更新实体时,出现以下错误

“Newtonsoft.Json.JsonSerializationException”类型的异常发生在 Newtonsoft.Json.dll 但未在用户代码中处理

附加信息:从“WellKnownValue”获取值时出错 'System.Data.Entity.Spatial.DbGeometry'。

内部异常似乎表明 WellKnownValue 为空,但事实并非如此,因为我检查了发送到服务器的 JSON,然后将其发送到 Breeze ContextProvider 并使用 SaveChanges 方法保存。

{
"entities": [
 {
  "TableKey": 2,
  "CaseName": "Mikhail Lermontov",
  "StartDate": "2013-06-11T00:00:00Z",
  "EndDate": null,
  "IsCurrent": true,
  "SRID": 109,
  "Shape": {
    "$id": "2",
    "$type": "System.Data.Entity.Spatial.DbGeometry, EntityFramework",
    "Geometry": {
      "$id": "3",
      "$type": "System.Data.Entity.Spatial.DbGeometryWellKnownValue, EntityFramework",
      "CoordinateSystemId": 2193,
      "WellKnownText": "POLYGON ((1695943 5462665, 1713098 5462665, 1713098 5449659, 1695943 5449659, 1695943 5462665))"
    }
  },
  "SpillLocation": "Marlborough Sounds",
  "Image": "http://www.nzmaritime.co.nz/images/lm5.jpg\r\n",
  "DefaultBaseMapKey": 2,
  "__unmapped": {
    "isPartial": false
  },
  "entityAspect": {
    "entityTypeName": "DatSpillCase:#Osiris.Model",
    "defaultResourceName": "DatSpillCases",
    "entityState": "Modified",
    "originalValuesMap": {
      "CaseName": "Mikhail Lermontov"
    },
    "autoGeneratedKey": {
      "propertyName": "TableKey",
      "autoGeneratedKeyType": "Identity"
    }
  }
}
 ],
  "saveOptions": {}
}

所以我的问题是,是否可以在 NewtonSoft 库中反序列化 DbGeometry 类型,如果没有,有什么建议可以解决这个问题。

【问题讨论】:

  • 我用来序列化和反序列化地理类型变量的是 GeoJSON。在这个地址 github.com/jbattermann/GeoJSON.Net 已经有一个不完整的 .Net 库,尽管仍然缺少一些东西,但您始终可以从该项目中编写自己的序列化器/反序列化器

标签: c# json breeze


【解决方案1】:

System.Data.Spatial.DbGeometry 不能很好地与Newtonsoft.Json 配合使用

您需要创建一个JsonConverter 来转换DbGeometry

public class DbGeometryConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType.IsAssignableFrom(typeof(string));
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject location = JObject.Load(reader);
            JToken token = location["Geometry"]["WellKnownText"];
            string value = token.ToString();

            DbGeometry converted = DbGeometry.PolygonFromText(value, 2193);
            return converted;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            // Base serialization is fine
            serializer.Serialize(writer, value);
        }
    }

然后在模型中的属性上添加属性

[JsonConverter(typeof(DbGeometryConverter))]
public DbGeometry Shape { get; set; }

现在,当您点击 BreezeController 时,反序列化将由我们的新 DbGeometryConverter 处理。

希望对你有帮助。

【讨论】:

  • 这种方法也适用于DbGeography,您只需将location["Geometry"]["WellKnownText"] 更改为location["Geography"]["WellKnownText"] 并将DbGeometry converted = DbGeometry.PolygonFromText(value, 2193); 更改为var converted = DbGeography.FromText(value);
【解决方案2】:

上面的答案很好用,但对 SRID (CoordinateSystemId) 2193 进行了硬编码。 然而,坐标系统 ID 可以出现在序列化数据中,如问题所示,或者它可以出现在 WellKnownText“SRID=2193;POINT (0 0)”中。此外,此方法只会读取多边形,但 WellKnownText 可以是很多东西,例如几何集合、点、线串等。要检索它,可以更新 ReadJson 方法以使用更通用的 FromText 方法,如下所示。 这是上面的类,更新了更通用的坐标系,也适用于任何几何类型。我还添加了地理版本以供参考。

public class DbGeometryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(string));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject location = JObject.Load(reader);
        JToken token = location["Geometry"]["WellKnownText"];
        string value = token.ToString();
        JToken sridToken = location["Geometry"]["CoordinateSystemId"];
        int srid;
        if (sridToken == null || int.TryParse(sridToken.ToString(), out srid) == false || value.Contains("SRID"))
        {
            //Set default coordinate system here.
            srid = 0;
        }

        DbGeometry converted;
        if (srid > 0)
            converted = DbGeometry.FromText(value, srid);
        else
            converted = DbGeometry.FromText(value);
        return converted;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // Base serialization is fine
        serializer.Serialize(writer, value);
    }
}

public class DbGeographyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(string));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject location = JObject.Load(reader);
        JToken token = location["Geography"]["WellKnownText"];
        string value = token.ToString();
        JToken sridToken = location["Geography"]["CoordinateSystemId"];
        int srid;
        if (sridToken == null || int.TryParse(sridToken.ToString(), out srid) == false || value.Contains("SRID"))
        {
            //Set default coordinate system here.
            //NOTE: Geography should always have an SRID, and it has to match the data in the database else all comparisons will return NULL!
            srid = 0;
        }
        DbGeography converted;
        if (srid > 0)
            converted = DbGeography.FromText(value, srid);
        else
            converted = DbGeography.FromText(value);
        return converted;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // Base serialization is fine
        serializer.Serialize(writer, value);
    }
}

【讨论】:

  • 我认为对于 DbGeographyConverter,您需要使用“Geography”字符串而不是“Geometry”来获取令牌和 sridToken。
【解决方案3】:

我不明白为什么不。就行了(DbGeometryWellKnownValue):

"$type": "System.Data.Entity.Spatial.DbGeometryWellKnownValue, EntityFramework",

应该是 (DbGeometry.WellKnownValue) 吗?

  "$type": "System.Data.Entity.Spatial.DbGeometry.WellKnownValue, EntityFramework",

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多