3

I have the following class with a static method in it,

public static class RouteSerializer
{
    public static string SerializeRouteInformation(string content)
    {
        RouteMapModel routeMapModel = new RouteMapModel();
        List<RouteMapModel.end_location> endLocationList = new List<RouteMapModel.end_location>();

        var obj = JObject.Parse(content);

        string objRoutes = obj["routes"].ToString();

        JArray routeArray = JArray.Parse(objRoutes);

        JArray legArray = new JArray();
        foreach (JObject item in routeArray)
        {
            string leg = item.GetValue("legs").ToString();                
            legArray.Add(leg);
        }

        JArray stepArray = new JArray();
        foreach (JObject item in legArray)
        {
            string step = item.GetValue("steps").ToString();
            stepArray.Add(step);
        }

        foreach(JObject item in stepArray)
        {
            string endLocation = item.GetValue("end_location").ToString();
            var serializedEndLocation = JsonConvert.DeserializeObject<RouteMapModel.end_location>(endLocation);
            endLocationList.Add(serializedEndLocation);
        }

        //... goes on
    }
}

But, I got an Error on the begging of the second foreach as it says Newtonsoft.Json.Linq.JValue cannot be assigned to Newtonsoft.Json.Linq.JObject. But the problem is, if I changed item type from JObject to JValue on the latter foreach clauses, I cannot be able to reach .GetValue() method as JValue doesn't inclued a one.

Below is the JSON string that I'm playing on,

    { "geocoded_waypoints" : [
      {
         "geocoder_status" : "OK",
         "place_id" : "ChIJ5cGzCGa4yhQRk-lsJUoyizk",
         "types" : [ "street_address" ]
      },
      {
         "geocoder_status" : "OK",
         "place_id" : "ChIJb5VnEF64yhQRCrgps2g77jc",
         "types" : [ "street_address" ]
      }
   ],
"routes" : [
      {
         "bounds" : {
            "northeast" : {
               "lat" : 40.990381,
               "lng" : 29.0282547
            },
            "southwest" : {
               "lat" : 40.9863897,
               "lng" : 29.0216118
            }
         },
         "copyrights" : "Harita verileri ©2019 Google",
         "legs" : [
            {
               "distance" : {
                  "text" : "0,9 km",
                  "value" : 894
               },
               "duration" : {
                  "text" : "11 dakika",
                  "value" : 655
               },
               "end_address" : "Caferağa Mahallesi, Tuğlacı Eminbey Cd. No:5, 34710 Kadıköy/İstanbul, Türkiye",
               "end_location" : {
                  "lat" : 40.9863897,
                  "lng" : 29.0218105
               },
               "start_address" : "Osmanağa Mahallesi, Serasker Cd. No:118, 34714 Kadıköy/İstanbul, Türkiye",
               "start_location" : {
                  "lat" : 40.9893375,
                  "lng" : 29.028225
               },
               "steps" : [
                  {
                     "distance" : {
                        "text" : "8 m",
                        "value" : 8
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 5
                     },
                     "end_location" : {
                        "lat" : 40.9894537,
                        "lng" : 29.0282117
                     },
                     "html_instructions" : "\u003cb\u003eZiya Bey Sk.\u003c/b\u003e adlı yerden \u003cb\u003eSerasker Cd.\u003c/b\u003e hedefine \u003cb\u003ekuzey\u003c/b\u003e yönünde ilerleyin",
                     "polyline" : {
                        "points" : "kvdyFkqdpDGCMA?F"
                     },
                     "start_location" : {
                        "lat" : 40.9893375,
                        "lng" : 29.028225
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "0,3 km",
                        "value" : 326
                     },
                     "duration" : {
                        "text" : "4 dakika",
                        "value" : 215
                     },
                     "end_location" : {
                        "lat" : 40.990381,
                        "lng" : 29.024582
                     },
                     "html_instructions" : "\u003cb\u003eSerasker Cd.\u003c/b\u003e yönünde \u003cb\u003esola\u003c/b\u003e dönün",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "awdyFiqdpDMxAKr@OjACP[vCEPC^Cf@CLAHENMZ@LQf@Qd@KZENSj@"
                     },
                     "start_location" : {
                        "lat" : 40.9894537,
                        "lng" : 29.0282117
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "34 m",
                        "value" : 34
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 25
                     },
                     "end_location" : {
                        "lat" : 40.9901185,
                        "lng" : 29.0243691
                     },
                     "html_instructions" : "\u003cb\u003eMühürdar Cd.\u003c/b\u003e yönünde \u003cb\u003esola\u003c/b\u003e dönün",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "{|dyFszcpDHFHF^X"
                     },
                     "start_location" : {
                        "lat" : 40.990381,
                        "lng" : 29.024582
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "8 m",
                        "value" : 8
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 5
                     },
                     "end_location" : {
                        "lat" : 40.990081,
                        "lng" : 29.0242851
                     },
                     "html_instructions" : "\u003cb\u003eMühürdar Cd.\u003c/b\u003e yönünde hafif \u003cb\u003esağa\u003c/b\u003e yönelin",
                     "maneuver" : "turn-slight-right",
                     "polyline" : {
                        "points" : "g{dyFiycpD?D?@?@@??@DB"
                     },
                     "start_location" : {
                        "lat" : 40.9901185,
                        "lng" : 29.0243691
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "7 m",
                        "value" : 7
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 6
                     },
                     "end_location" : {
                        "lat" : 40.9900274,
                        "lng" : 29.0243112
                     },
                     "html_instructions" : "\u003cb\u003eMuvakkıthane Cd.\u003c/b\u003e konumunda \u003cb\u003esola\u003c/b\u003e dönün",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "_{dyFyxcpDDABA"
                     },
                     "start_location" : {
                        "lat" : 40.990081,
                        "lng" : 29.0242851
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "0,4 km",
                        "value" : 352
                     },
                     "duration" : {
                        "text" : "4 dakika",
                        "value" : 259
                     },
                     "end_location" : {
                        "lat" : 40.987586,
                        "lng" : 29.0217648
                     },
                     "html_instructions" : "\u003cb\u003eMühürdar Cd.\u003c/b\u003e yönünde \u003cb\u003esağa\u003c/b\u003e dönün",
                     "maneuver" : "turn-right",
                     "polyline" : {
                        "points" : "uzdyF}xcpDhAz@pA|@pBtAHRJHRPPNPX\\d@BDHKfAlBDLDHDN@LBN"
                     },
                     "start_location" : {
                        "lat" : 40.9900274,
                        "lng" : 29.0243112
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "63 m",
                        "value" : 63
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 51
                     },
                     "end_location" : {
                        "lat" : 40.987087,
                        "lng" : 29.0216118
                     },
                     "html_instructions" : "\u003cb\u003eMühürdar Cd.\u003c/b\u003e boyunca ilerlemek için \u003cb\u003esola\u003c/b\u003e dönün",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "mkdyF_icpDNBJ@J@TBJ?JADA@DDN"
                     },
                     "start_location" : {
                        "lat" : 40.987586,
                        "lng" : 29.0217648
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "74 m",
                        "value" : 74
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 64
                     },
                     "end_location" : {
                        "lat" : 40.9864708,
                        "lng" : 29.0216118
                     },
                     "html_instructions" : "\u003cb\u003eMühürdar Cd.\u003c/b\u003e boyunca ilerlemek için \u003cb\u003esola\u003c/b\u003e dönün",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "ihdyFahcpDJEJAFALAH?VBj@F"
                     },
                     "start_location" : {
                        "lat" : 40.987087,
                        "lng" : 29.0216118
                     },
                     "travel_mode" : "WALKING"
                  },
                  {
                     "distance" : {
                        "text" : "22 m",
                        "value" : 22
                     },
                     "duration" : {
                        "text" : "1 dakika",
                        "value" : 25
                     },
                     "end_location" : {
                        "lat" : 40.9863897,
                        "lng" : 29.0218105
                     },
                     "html_instructions" : "\u003cb\u003eTuğlacı Eminbey Cd.\u003c/b\u003e yönünde \u003cb\u003esola\u003c/b\u003e dönün",
                     "maneuver" : "turn-left",
                     "polyline" : {
                        "points" : "mddyFahcpDNg@"
                     },
                     "start_location" : {
                        "lat" : 40.9864708,
                        "lng" : 29.0216118
                     },
                     "travel_mode" : "WALKING"
                  }
               ],
               "traffic_speed_entry" : [],
               "via_waypoint" : []
            }
         ],
         "overview_polyline" : {
            "points" : "kvdyFkqdpDUEM`B{@hHQfBGXMZ@Lc@lAQj@Sj@HFHF^X?D?B@@DBDABAhAz@bErCHRJHd@`@n@~@BDHKlAzBJXD\\ZD`@DVADA@DDNJERCVAbAJNg@"
         },
         "summary" : "Serasker Cd. ve Mühürdar Cd.",
         "warnings" : [
            "Yürüyerek gitmek için yol tarifi beta özelliğinde mevcuttur. Dikkat – Bu rotada kaldırım veya yaya yolu olmayabilir."
         ],
         "waypoint_order" : []
      }
   ],
   "status" : "OK"
}

I do not prefer to deserialize the whole object as service is not going to use nearly all these properties except "legs", "steps", and "end_location" which is inside of "steps".

dbc
  • 104,963
  • 20
  • 228
  • 340
RaZzLe
  • 1,984
  • 3
  • 13
  • 24
  • 1
    *"I do not prefer to deserialize the whole object"* - You're making it more difficult than it has to be. `JObject.Parse(content)` is deserializing the whole thing already. Just traverse the object graph instead of converting back to string and reparsing. – madreflection Feb 19 '19 at 16:45

1 Answers1

2

The problem is that your code to query selected portions of the JObject obj hierarchy repeatedly converts back and forth from JToken to JSON string representations, and at one point, rather than re-parsing a JSON string, you simply use it as a string literal.

Specifically, the problem is with the following code:

JArray legArray = new JArray();
foreach (JObject item in routeArray)
{
    string leg = item.GetValue("legs").ToString();
    legArray.Add(leg);
}

You are calling JArray.Add(), but this method has multiple overloads, so which one is called? Because there is an implicit operator from string to JToken, what happens is that the leg string gets converted to a JValue string literal using the implicit operator, which in turn is added to the JArray. Afterwards the following code fails with an invalid cast exception, since the items you added to legArray are of type JValue not JObject:

foreach (JObject item in legArray)
{

The solution is to simplify your code and completely avoid converting back and forth between string and JToken representations. The following code does the trick:

var endLocationList = obj
    .SelectTokens("routes[*].legs[*].steps[*].end_location")
    .Select(t => t.ToObject<Location>())
    .ToList();

Using the type

public class Location
{
    public double Lat { get; set; }
    public double Lng { get; set; }
}

Notes:

  • You can deserialize directly from a JToken to a POCO using JToken.ToObject<T>(). This is simpler and more performant that formatting the JToken as a string then deserializing the string.

  • JToken.SelectTokens() allows for querying the JSON hierarchy using JSONPath syntax.

    Here [*] represents a wildcard selecting all items in an array, specifically the "routes", "legs" and "steps" arrays.

    For further information see # JSONPath - XPath for JSON.

Demo fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • this works perfectly on my local machine and my return model is like this `[{"Legs":[{"Steps":[{"EndLocation":[{"Lat":41.0917854,"Lng":29.0938952},{"Lat":41.0927256,"Lng":29.0943774},{"Lat":41.0931956,"Lng":29.094848},{"Lat":41.094488299999988,"Lng":29.0952805},{"Lat":41.0945246,"Lng":29.0954362},{"Lat":41.0945706,"Lng":29.0955857},{"Lat":41.094365499999988,"Lng":29.0967187}]}]}]}]` But, after I publish this API project to server and send the very same request to the method which returns this model, it returns the model without filling the EndLocation list – RaZzLe Feb 24 '19 at 20:13
  • Live project returns this: `[{"Legs":[{"Steps":[{"EndLocation":[]}]}]}]` The strange part is, I have also tested on a different server, and it fills the model as intended. Do you have any idea how this could be happening? – RaZzLe Feb 24 '19 at 20:14
  • @RaZzLe - need to see a [mcve] but maybe you're not configuring your server to use camel case? See maybe [How can I return camelCase JSON serialized by JSON.NET from ASP.NET MVC controller methods?](https://stackoverflow.com/q/19445730/3744182) or [asp.net core 1.0 web api use camelcase](https://stackoverflow.com/q/38139607/3744182). – dbc Feb 26 '19 at 21:28