ssimonton
ssimonton

Reputation: 11

JsonConvert.DeserializeObject with NetTopologySuite.IO.GeoJSON has exception Expected token '}' not found

I have the following Data Transfer Object

[DataContract]
    public class Entity : IEntity
    {
        /// <summary>
        /// 
        /// </summary>
        public Entity() { }

        /// <summary>
        /// 
        /// 
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("ENAME")]
        [Display(Name = "ENAME")]
        public string ENAME { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("ENTITY_DEF_I_EID")]
        [Display(Name = "Entity Def EID")]
        public int ENTITY_DEF_I_EID { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("I_SID")]
        [Display(Name = "Server ID")]
        public int I_SID { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("I_EID")]
        [Display(Name = "Entity ID")]
        public int I_EID { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("F_DELETED")]
        [Display(Name = "Deleted")]
        public string F_DELETED { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("S_LAST_MOD_INFO")]
        [Display(Name = "Last Modified Into")]
        public string S_LAST_MOD_INFO { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM1")]
        [Display(Name = "Custom 1")]
        public string CUSTOM1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM2")]
        [Display(Name = "Custom 2")]
        public string CUSTOM2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM3")]
        [Display(Name = "Custom 3")]
        public string CUSTOM3 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM4")]
        [Display(Name = "Custom 4")]
        public string CUSTOM4 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM5")]
        [Display(Name = "Custom 5")]
        public string CUSTOM5 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM6")]
        [Display(Name = "Custom 6")]
        public string CUSTOM6 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM7")]
        [Display(Name = "Custom 7")]
        public string CUSTOM7 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM8")]
        [Display(Name = "Custom 8")]
        public string CUSTOM8 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM9")]
        [Display(Name = "Custom 9")]
        public string CUSTOM9 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("CUSTOM10")]
        [Display(Name = "Custom 10")]
        public string CUSTOM10 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("RESV1")]
        [Display(Name = "Reserved 1")]
        public string RESV1 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("RESV2")]
        [Display(Name = "Reserved 2")]
        public string RESV2 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("RESV3")]
        [Display(Name = "Reserved 3")]
        public string RESV3 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("RESV4")]
        [Display(Name = "Reserved 4")]
        public string RESV4 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("RESV5")]
        [Display(Name = "Reserved 5")]
        public string RESV5 { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("FEATURE_DEF_NAME")]
        [Display(Name = "Feature Def Name")]
        public string FEATURE_DEF_NAME { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("PHOTO")]
        [Display(Name = "Photo")]
        public string PHOTO { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("DETAIL")]
        [Display(Name = "Detail")]
        public string DETAIL { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("DOCUMENT")]
        [Display(Name = "Document")]
        public string DOCUMENT { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("ACAD_COLOR")]
        [Display(Name = "Acad Color")]
        public decimal ACAD_COLOR { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("ACAD_LAYER")]
        [Display(Name = "Layer")]
        public string ACAD_LAYER { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("ATT_BLK")]
        [Display(Name = "Attribute Block")]
        public string ATT_BLK { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("ATT_LAY")]
        [Display(Name = "Attribute Layer")]
        public string ATT_LAY { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("TAG_ROT")]
        [Display(Name = "Tag Rotation")]
        public decimal TAG_ROT { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("X_COORD")]
        [Display(Name = "X Coordinate")]
        public decimal X_COORD { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("Y_COORD")]
        [Display(Name = "Y Coordinate")]
        public decimal Y_COORD { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        [JsonPropertyName("Z_COORD")]
        [Display(Name = "Z Coordinate")]
        public decimal Z_COORD { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [DataMember]
        //[JsonPropertyName("GEOM")]
        //[JsonProperty(PropertyName = "GEOM", ItemConverterType = typeof(GeometryConverter))]
        [Newtonsoft.Json.JsonConverter(typeof(GeometryConverter))]
        [Display(Name = "Geometry")]
        public Geometry GEOM { get; set; }
    }

I have a REST Server that has the following method that is responsible for querying a PostgreSQL database that returns the Data Transfer Object to the calling client.

/// <summary>
        /// Returns detailed information about the feature including the Latitude and Longitude data given an ename
        /// </summary>
        /// <param name="ServerDefName">Server Definition Name that will be used to build the connection string to the database</param>
        /// <param name="DataSetName">Dataset Name that will be used to build the connection string to the database</param>
        /// <param name="ENAME"></param>
        /// <returns>IQueryable(xxxxxxxxDTO)</returns>
        [HttpGet, Route("Entity")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public ActionResult Entity(string ServerDefName, string DataSetName, string ENAME)
        {
            try
            {
                sbLogger.Info("Web Service - " + MethodBase.GetCurrentMethod() + "(" + ServerDefName + ", " + DataSetName + ", " + ENAME + ")");

                SetConnectionString(ServerDefName, DataSetName);

                var query = new GetEntityDetailQuery(ConnectionString, DataUser, ENAME);
                var handler = SpatialBASEQueryHandlerFactory.Build(query);

                //Get the DataTable
                DataTable dt = handler.Get();

                var results = ConvertEntityDataTable(dt);
                
                //Serialize the Result
                SerializeResult(results);

                return Ok(results);
            }
            catch (Exception ex)
            {
                sbLogger.Warn("An Exception occured in " + MethodBase.GetCurrentMethod() + "!!", ex);
                var result = StatusCode(StatusCodes.Status500InternalServerError, ex);
                return result;
            }
        }

The following is the JSON result that is returned inside of the ActionResult

{
        "clli": null,
        "cT_CONAME": "AIRPORT DLC(SIC)",
        "cT_EXCH": null,
        "relateD_PORTS": null,
        "switcH_ID": null,
        "maP_LATITUDE": -157.09853,
        "maP_LONGITUDE": 21.1574725,
        "acaD_BLOCK": "COFFICE",
        "acaD_HAND": "5DF7",
        "ename": "0:25:0:4919524",
        "entitY_DEF_I_EID": 25,
        "i_SID": 0,
        "i_EID": 4919524,
        "f_DELETED": "N",
        "s_LAST_MOD_INFO": "USER1:20100918110317",
        "custoM1": null,
        "custoM2": null,
        "custoM3": null,
        "custoM4": null,
        "custoM5": null,
        "custoM6": null,
        "custoM7": null,
        "custoM8": null,
        "custoM9": null,
        "custoM10": null,
        "resV1": null,
        "resV2": null,
        "resV3": null,
        "resV4": null,
        "resV5": null,
        "featurE_DEF_NAME": "CO",
        "photo": null,
        "detail": null,
        "document": null,
        "acaD_COLOR": 256.0,
        "acaD_LAYER": "B-LNDMRK",
        "atT_BLK": null,
        "atT_LAY": null,
        "taG_ROT": 0.00256882745527,
        "x_COORD": -157.09854,
        "y_COORD": 21.1575195,
        "z_COORD": 0.0,
        "geom": {
            "type": "Point",
            "coordinates": {
                "m": 0.0,
                "z": 0.0,
                "x": -157.09853,
                "y": 21.1574725
            }
        }
    },
    {
        "clli": null,
        "cT_CONAME": null,
        "cT_EXCH": null,
        "relateD_PORTS": null,
        "switcH_ID": null,
        "maP_LATITUDE": -156.3413802,
        "maP_LONGITUDE": 20.725685900000003,
        "acaD_BLOCK": "COFFICE",
        "acaD_HAND": "11771",
        "ename": "0:25:0:4919525",
        "entitY_DEF_I_EID": 25,
        "i_SID": 0,
        "i_EID": 4919525,
        "f_DELETED": "N",
        "s_LAST_MOD_INFO": "USER2:20100918110317",
        "custoM1": null,
        "custoM2": null,
        "custoM3": null,
        "custoM4": null,
        "custoM5": null,
        "custoM6": null,
        "custoM7": null,
        "custoM8": null,
        "custoM9": null,
        "custoM10": null,
        "resV1": null,
        "resV2": null,
        "resV3": null,
        "resV4": null,
        "resV5": null,
        "featurE_DEF_NAME": "CO",
        "photo": null,
        "detail": null,
        "document": null,
        "acaD_COLOR": 256.0,
        "acaD_LAYER": "B-LNDMRK",
        "atT_BLK": null,
        "atT_LAY": null,
        "taG_ROT": -0.00187324353795,
        "x_COORD": -156.34147,
        "y_COORD": 20.7256984,
        "z_COORD": 0.0,
        "geom": {
            "type": "Point",
            "coordinates": {
                "m": 0.0,
                "z": 0.0,
                "x": -156.3413802,
                "y": 20.725685900000003
            }
        }
    }

When the ActionResult is returned to the client the following method is where the exception occurs.

Private Function GetReturnList(ByVal objects As String) As IEnumerable(Of Entity)
    Dim _listReturn As IEnumerable(Of Entity)
    Dim _listEntity As List(Of Entity) = New List(Of Entity)

    Dim settings As JsonSerializerSettings = New JsonSerializerSettings()
    settings.TypeNameHandling = TypeNameHandling.Auto
    settings.NullValueHandling = NullValueHandling.Ignore

    If Not IsNothing(objects) Then
        _listEntity = JsonConvert.DeserializeObject(Of List(Of Entity))(objects, settings).AsEnumerable()
        Dim obj As Entity = _listEntity(0)

        Select Case obj.FEATURE_DEF_NAME
            Case "CO", "REMOTE_CO"
                _listReturn = JsonConvert.DeserializeObject(Of List(Of CT_CO))(objects, settings).Cast(Of Entity).AsEnumerable()

            Case "NETWORK_INTERFACE", "STATION_PROTECTOR"
                _listReturn = JsonConvert.DeserializeObject(Of List(Of CT_SBFAC))(objects, settings).Cast(Of Entity).AsEnumerable()

            Case "WORKORDER"
                _listReturn = JsonConvert.DeserializeObject(Of List(Of CT_WORKS))(objects, settings).Cast(Of Entity).AsEnumerable()

            Case "CUSTOMER"
                _listReturn = JsonConvert.DeserializeObject(Of List(Of CUSTOMER))(objects, settings).Cast(Of Entity).AsEnumerable()

            Case "FIBERS"
                _listReturn = JsonConvert.DeserializeObject(Of List(Of FIBERS))(objects, settings).Cast(Of Entity).AsEnumerable()

            Case "FIBER_SPLITTER"
                _listReturn = JsonConvert.DeserializeObject(Of List(Of FO_SPLITTER))(objects, settings).Cast(Of Entity).AsEnumerable()
        End Select

    End If


    Return _listReturn
End Function

The Exception occurs where I try to Deserialize the json string objects on the following line:

_listEntity = JsonConvert.DeserializeObject(Of List(Of Entity))(objects, settings).AsEnumerable()

The Exception message is Expected token '}' not found.

The following is the stack trace of the Exception that is thrown.

   at NetTopologySuite.IO.Converters.GeometryConverter.ParseGeometry(JsonReader reader, JsonSerializer serializer)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at CtPortalServices.Queries.Handler.GetLocateFeaturesQueryHandler.GetReturnList(String objects) in C:\Development\Matador\Spatial E-Suite\11.0\Clients\Commander\CtPortalServices\Domain\Queries\Handler\Locate\GetLocateFeaturesQueryHandler.vb:line 108
   at CtPortalServices.Queries.Handler.GetLocateFeaturesQueryHandler.GetReturnObject(IRestResponse response) in C:\Development\Matador\Spatial E-Suite\11.0\Clients\Commander\CtPortalServices\Domain\Queries\Handler\Locate\GetLocateFeaturesQueryHandler.vb:line 90
   at CtPortalServices.Queries.QueryHandler.Get() in C:\Development\Matador\Spatial E-Suite\11.0\Clients\Commander\CtPortalServices\Domain\Queries\QueryHandler.vb:line 237

Can someone please point me in the right direction on how to resolve this?

Thank you in advance!

Upvotes: 0

Views: 1209

Answers (2)

ssimonton
ssimonton

Reputation: 11

Here is what fixed my DeserializeObject issue.

It had everything to do with the way the default serializer was handling the individual geometry types.

I added the following code to my ConfigureServices method.

services.AddControllers(options => {
                    options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Point)));
                    options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Coordinate)));
                    options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(LineString)));
                    options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(MultiLineString)));
                }).AddNewtonsoftJson(options => {
                    foreach (var converter in NetTopologySuite.IO.GeoJsonSerializer.Create(new GeometryFactory(new PrecisionModel(), 4326)).Converters)
                    {
                        options.SerializerSettings.Converters.Add(converter);
                    }
                }).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

Now the JSON string on the client side is well formed and the coordinates are in the correct form.

"geom": {
            "type": "Point",
            "coordinates": [
                -157.09853,
                21.1574725
            ]
        }

Upvotes: 0

Luke Woodward
Luke Woodward

Reputation: 64969

Your GeoJSON is not well-formed, or you are trying to use a GeoJSON converter to convert data that is not in GeoJSON format.

According to RFC 7946 section 3.1, the value of the coordinates property should not be an object, it should be an array. For example,

            "coordinates": {
                "m": 0.0,
                "z": 0.0,
                "x": -156.3413802,
                "y": 20.725685900000003
            }

should be

            "coordinates": [-156.3413802, 20.725685900000003, 0.0]

(I'm not sure what you would do with the m value, but in both your sample points it is zero so I'm hoping it's okay to ignore it.)

The RFC contains further examples of GeoJSON.

I don't know whether you have the ability to change the coordinates being returned to your REST API, but if not, you may have to parse the JSON some other way.

Sadly however it seems the NetTopologySuite GeoJson converter isn't very good at handling this situation. Once it starts processing the first of your coordinates properties, it seems it keeps processing JSON tokens until it hits the end-of-array character at the end of your JSON data.

Upvotes: 1

Related Questions