Deserializing a JSON value from an API - curly vs. square brackets - json

I am working to integrate some weather info from an API where I get a return value like this ...
{"coord":{"lon":-85.6,"lat":43.05},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"stations","main":{"temp":277.33,"pressure":1010,"humidity":65,"temp_min":276.15,"temp_max":279.15},"visibility":16093,"wind":{"speed":5.7,"deg":100,"gust":8.2},"clouds":{"all":75},"dt":1524076680,"sys":{"type":1,"id":1410,"message":0.0043,"country":"US","sunrise":1524048860,"sunset":1524097768},"id":420018526,"name":"Grand Rapids","cod":200}
I have created a built_value and built_collection object to deserialize into. If I build the object "by hand" and serialize it, and JSON encode it ...
var w = new Welcome((b) => b
..id = 420018526
..name = "Grand Rapids"
..weather.add(new Weather((w)=>w
..id = 803
..main = "Clouds"
..description = "broken clouds"
..icon = "04d"
))
..cod = 200
..coord.lat = 43.05
..coord.lon= -85.6
..base = 'stations'
..main.temp = 277.33
..main.pressure = 1010
..main.humidity = 65
..main.temp_min = 276.15
..main.temp_max = 279.15
..visibility = 16093
..wind.speed = 5.7
..wind.deg = 100
..wind.gust = 8.2
..clouds.all = 75
..dt = 1524076680
..sys.type = 1
..sys.id = 1410
..sys.message = 0.0043
..sys.country = "US"
..sys.sunrise = 1524048860
..sys.sunset = 1524097768
);
var ws = serializers.serialize(w);
var wsj = JSON.encode(ws);
I get something like this ...
["Welcome","coord",["lon",-85.6,"lat",43.05],"weather",[["id",803,"main","Clouds","description","broken clouds","icon","04d"]],"base","stations","main",["temp",277.33,"pressure",1010,"humidity",65,"temp_min",276.15,"temp_max",279.15],"visibility",16093,"wind",["speed",5.7,"deg",100,"gust",8.2],"clouds",["all",75],"dt",1524076680,"sys",["type",1,"id",1410,"message",0.0043,"country","US","sunrise",1524048860,"sunset",1524097768],"id",420018526,"name","Grand Rapids","cod",200]
... which looks amazingly similar, but you'll notice that the dart:convert JSON libraries seem to prefer square brackets [ over curly ones {
When I try to deserialze the curly brackets wsjdA = JSON.decode(wJson); I get an error
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Welcome' of 'wsjdA' where
_InternalLinkedHashMap is from dart:collection
String is from dart:core
Welcome is from package:ex_models/src/weather.dart
But if I do this goofy thing (to parse out the square brackets for curly and add the first string "Welcome" ...
var wJsonB= '["Welcome",' + wJson
.replaceAll('{', '[')
.replaceAll('}', ']')
.replaceAll(':', ',')
.substring(1);
... it works fine
Am I missing something - is there an easier way to do the JSON/Object deserialization in dart. I'd really like to be able to parse a valid JSON API response into a dart object, in a standard way, without the manual parsing.
I think this may have worked if it weren't for the list of Weather objects within the Root/Welcome object. Because this object contains a list of objects, this is confusing things a bit? This is a BuiltList in my object.
I am already using a built_value object with built_collections and using the dart:convert libraries. Wondering if there is something I am missing?

[] is for lists/arrays, {} is for maps. With built_value you can use properties of type BuiltList and BuiltMap.
JSON.decode(wJson); doesn't return instances of classes, it just makes lists, maps, and separate primitive values out of a JSON string.
built_value has it's own serialization from what JSON.decode(...) returns to concrete instances of classes.
built_value by default has a reduced JSON format that won't be compatible with your JSON. For that you need to register the StandardJsonPlugin (see also https://github.com/google/built_value.dart/issues/171)
For details about serialization with built_value see https://medium.com/dartlang/darts-built-value-for-serialization-f5db9d0f4159.

Related

LINQ on JArray always returning null

I'm trying to parse some Json in Xamarin.Forms
I'm pretty new to Xamarin, though not to .net
Here's my simple dimple code
var htc = new HttpClient();
var rsp = await htc.GetStringAsync("myurl.com");
JArray lists = JArray.Parse(rsp);
var c = lists.Count();
var l = lists.ToList();
var w=lists.Where(x => true);
Even though c returns the correct count of items in the list, l & w are both null
How come? and how do I fix it?
Thanks!
PS. What I'm really trying to do is bind a ListView to a JArray, but it seems impossible directly,(Text={Binding MyPropertyName} crashes the app). so I'm trying to run a Select on the JArray to convert to a KeyValuePair. If you have any ideas to bind directly, that would be best!
UPDATE
The issue seems even odder
I tried this
var kvlist = new List<KeyValuePair<string, string>>();
foreach (JObject ll in lists)
{
kvlist.Add(new KeyValuePair<string, string>(ll["Name"].ToString(), ll["Name"].ToString()));
}
Here at least the iteration works nicely, but the kvlist is null the entire time. Trying to evaluate the kvlist variable, I get:
Unable to cast object of type 'System.RuntimeType' to type
'Mono.Debugger.Soft.TypeMirror'.
What can the matter be?
Thanks again!
You should not directly call .ToList on object type of JArray rather you should Select List of type you need. For ex.
var l = lists.Select(c => new MyList
{
Item1 = c.Value<int>("ItemName1"),
Item2 = c.Value<string>("ItemName2")
}).ToList(); //Replce MyList with your class name
On the second case where w is null, after getting list l you need to specify attribute, based of what you are using where clause. For ex.
var w=l.Where(x =>x.isAdmin==true); //l is list you selected above
Hope it help you.
Solution:
You can use code below to convert a JArray to a list<T>:
List<T> t =lists.ToObject<List<T>>();
Refer: https://www.newtonsoft.com/json/help/html/ToObjectType.htm
You could also use JsonConvert.DeserializeObject to convert it directly into the desired type. You have to define a jsonModel class with the same structure of your json fisrtly.
List<jsonModel> modelList = JsonConvert.DeserializeObject<List<jsonModel>>(jsonStr);
Refer :https://www.newtonsoft.com/json/help/html/SerializingCollections.htm
The documentation seems to indicate that JArray has properties for .Count, but no overload method because it does not implement IEnumerable, however as alluded to in the comments, it does implement the JToken type (which JArray is a collection of) and implements IEnumerable.
See the following documentation for JToken: https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JToken.htm
and JArray respectively:
https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JArray.htm
The preferred mechanism is to create a strong type and then run .ToObject();
You can access JArray.ChildrenTokens which may help

Is there a XPath with schema equivilent for JSON? [duplicate]

I have JSON as a string and a JSONPath as a string. I'd like to query the JSON with the JSON path, getting the resulting JSON as a string.
I gather that Jayway's json-path is the standard. The online API, however, doesn't have have much relation to the actual library you get from Maven. GrepCode's version roughly matches up though.
It seems like I ought to be able to do:
String originalJson; //these are initialized to actual data
String jsonPath;
String queriedJson = JsonPath.<String>read(originalJson, jsonPath);
The problem is that read returns whatever it feels most appropriate based on what the JSONPath actually finds (e.g. a List<Object>, String, double, etc.), thus my code throws an exception for certain queries. It seems pretty reasonable to assume that there'd be some way to query JSON and get JSON back; any suggestions?
Java JsonPath API found at jayway JsonPath might have changed a little since all the above answers/comments. Documentation too. Just follow the above link and read that README.md, it contains some very clear usage documentation IMO.
Basically, as of current latest version 2.2.0 of the library, there are a few different ways of achieving what's been requested here, such as:
Pattern:
--------
String json = "{...your JSON here...}";
String jsonPathExpression = "$...your jsonPath expression here...";
J requestedClass = JsonPath.parse(json).read(jsonPathExpression, YouRequestedClass.class);
Example:
--------
// For better readability: {"store": { "books": [ {"author": "Stephen King", "title": "IT"}, {"author": "Agatha Christie", "title": "The ABC Murders"} ] } }
String json = "{\"store\": { \"books\": [ {\"author\": \"Stephen King\", \"title\": \"IT\"}, {\"author\": \"Agatha Christie\", \"title\": \"The ABC Murders\"} ] } }";
String jsonPathExpression = "$.store.books[?(#.title=='IT')]";
JsonNode jsonNode = JsonPath.parse(json).read(jsonPathExpression, JsonNode.class);
And for reference, calling 'JsonPath.parse(..)' will return an object of class 'JsonContent' implementing some interfaces such as 'ReadContext', which contains several different 'read(..)' operations, such as the one demonstrated above:
/**
* Reads the given path from this context
*
* #param path path to apply
* #param type expected return type (will try to map)
* #param <T>
* #return result
*/
<T> T read(JsonPath path, Class<T> type);
Hope this help anyone.
There definitely exists a way to query Json and get Json back using JsonPath.
See example below:
String jsonString = "{\"delivery_codes\": [{\"postal_code\": {\"district\": \"Ghaziabad\", \"pin\": 201001, \"pre_paid\": \"Y\", \"cash\": \"Y\", \"pickup\": \"Y\", \"repl\": \"N\", \"cod\": \"Y\", \"is_oda\": \"N\", \"sort_code\": \"GB\", \"state_code\": \"UP\"}}]}";
String jsonExp = "$.delivery_codes";
JsonNode pincodes = JsonPath.read(jsonExp, jsonString, JsonNode.class);
System.out.println("pincodesJson : "+pincodes);
The output of the above will be inner Json.
[{"postal_code":{"district":"Ghaziabad","pin":201001,"pre_paid":"Y","cash":"Y","pickup":"Y","repl":"N","cod":"Y","is_oda":"N","sort_code":"GB","state_code":"UP"}}]
Now each individual name/value pairs can be parsed by iterating the List (JsonNode) we got above.
for(int i = 0; i< pincodes.size();i++){
JsonNode node = pincodes.get(i);
String pin = JsonPath.read("$.postal_code.pin", node, String.class);
String district = JsonPath.read("$.postal_code.district", node, String.class);
System.out.println("pin :: " + pin + " district :: " + district );
}
The output will be:
pin :: 201001 district :: Ghaziabad
Depending upon the Json you are trying to parse, you can decide whether to fetch a List or just a single String/Long value.
Hope it helps in solving your problem.
For those of you wondering why some of these years-old answers aren't working, you can learn a lot from the test cases.
As of September 2018, here's how you can get Jackson JsonNode results:
Configuration jacksonConfig = Configuration.builder()
.mappingProvider( new JacksonMappingProvider() )
.jsonProvider( new JacksonJsonProvider() )
.build();
JsonNode node = JsonPath.using( jacksonConfig ).parse(jsonString);
//If you have a json object already no need to initiate the jsonObject
JSONObject jsonObject = new JSONObject();
String jsonString = jsonObject.toString();
String path = "$.rootObject.childObject"
//Only returning the child object
JSONObject j = JsonPath.read(jsonString, path);
//Returning the array of string type from the child object. E.g
//{"root": "child":[x, y, z]}
List<String> values = sonPath.read(jsonString, path);
Check out the jpath API. It's xpath equivalent for JSON Data. You can read data by providing the jpath which will traverse the JSON data and return the requested value.
This Java class is the implementation as well as it has example codes on how to call the APIs.
https://github.com/satyapaul/jpath/blob/master/JSONDataReader.java
Readme -
https://github.com/satyapaul/jpath/blob/master/README.md

JSON String parsing each character as an object

I have a JSON file that contains what I believe to be a correct JSON string:
{"title": "exampleTitle", "tipTitle": "exampleTipTitle", "tip": "exampleTip"}
I'm trying to parse said file and take out the 3 values then store them in variables, however currently, it parses each individual character as a separate object, therefore:
JSONobj[1] = "
and so on. Assuming that currentLocation = the directory location of the json file.
Code
var jsonLocation = currentLocation + "json.txt";
var request = new XMLHttpRequest();
request.open("GET", jsonLocation, false);
request.send(null);
var returnValue = request.responseText;
var JSONobj = JSON.parse(JSON.stringify(returnValue));
var headerTitle = JSONobj[0];
A few clarifications, the stringify is in because it was throwing an unexpected token error. I've tried changing the file tile to .json instead but that also makes no difference. "It also gives off a XMLHttpRequest on the main thread is deprecated" but I'm not particularly sure how to solve that issue. Any help would be appreciated.
var returnValue = request.responseText;
Here returnValue is a string of JSON.
"{\"title\": \"exampleTitle\", \"tipTitle\": \"exampleTipTitle\", \"tip\": \"exampleTip\"}
var JSONobj = JSON.parse(JSON.stringify(returnValue));
Here you convert the string of JSON to JSON. So you have a JSON string representing a string, and that string is a representation of a data structure in JSON.
"\"{\\"title\\": \\"exampleTitle\\", \\"tipTitle\\": \\"exampleTipTitle\\", \\"tip\\": \\"exampleTip\\"}"
Then you parse it and convert it back to the original string of JSON.
"{\"title\": \"exampleTitle\", \"tipTitle\": \"exampleTipTitle\", \"tip\": \"exampleTip\"}
So you end up back where you start.
Just don't use JSON.stringify here, and you'll convert your JSON to a JavaScript object:
var javascript_object = JSON.parse(returnValue);
Then you have an object, but it doesn't have a 0 property so it doesn't make sense to access it with javascript_object[0]. The properties have names, such as javascript_object.title.
Your JSON doesn't describe an array, so indexing into it with an index like 0 doesn't make sense. Your JSON describes an object, which will have properties with the names title, tipTitle, and tip.
Additionally, you're overdoing your parsing: You just want to parse, not stringify (which is the opposite of parsing):
var JSONobj = JSON.parse(returnValue);
So:
var JSONobj = JSON.parse(returnValue);
var headerTitle = JSONobj.title;
console.log(headerTitle); // "exampleTitle"
Side note: By the time you've assigned it to the variable you've called JSONobj, it isn't JSON anymore, it's just a normal JavaScript object, so that name is a bit misleading. If you're writing source code, and you're not dealing with a string, you're not dealing with JSON anymore. :-)

Couchbase - deserialize json into dynamic type

I'm trying to deserialize some JSON coming back from couchbase into a dynamic type.
The document is something like this so creating a POCO for this would be overkill:
{
UsersOnline: 1
}
I figured that something like this would do the trick, but it seems to deserialize into a dynamic object with the value just being the original JSON
var jsonObj = _client.GetJson<dynamic>(storageKey);
results in:
jsonObj { "online": 0 }
Is there anyway I can get the couchbase deserializer to generate the dynamic type for me?
Cheers
The default deserializer for the client uses .NET's binary serializer, so when you save or read a JSON string, it's just a string. GetJson will always just return a string. However, there are a couple of options:
You could convert JSON records to Dictionary instances:
var appJson = "{ \"UsersOnline\" : 1, \"NewestMember\" : \"zblock\" }";
var result = client.ExecuteStore(StoreMode.Set, "userCount", appJson);
var item = client.GetJson<Dictionary<string, object>>("userCount");
Console.WriteLine("There are {0} users online. The newest member is {1}.",
item["UsersOnline"], item["NewestMember"]);
Or you could use a dynamic ExpandoObject instance:
var appJson = "{ \"UsersOnline\" : 1, \"NewestMember\" : \"zblock\" }";
var result = client.ExecuteStore(StoreMode.Set, "userCount", appJson);
dynamic item = client.GetJson<ExpandoObject>("userCount");
Console.WriteLine("There are {0} users online. The newest member is {1}.",
item.UsersOnline, item.NewestMember);
In either case you're losing static type checking, which seems like it's OK for your purposes. In both cases you get access to the JSON properties without having to parse the JSON into a POCO though...
Edit: I wrote a couple of extension methods that may be useful and blogged about them at http://blog.couchbase.com/moving-no-schema-stack-c-and-dynamic-types

How to JSON serialize math vector type in F#?

I'm trying to serialize "vector" (Microsoft.FSharp.Math) type. And I get that error:
Exception Details: System.Runtime.Serialization.SerializationException: Type 'Microsoft.FSharp.Math.Instances+FloatNumerics#115' with data contract name 'Instances.FloatNumerics_x0040_115:http://schemas.datacontract.org/2004/07/Microsoft.FSharp.Math' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
I have tried to put KnownType attribute and some other stuff, but nothing helps!
Could someone know the answer?
This is the code I use:
// [< KnownType( typeof<vector> ) >]
type MyType = vector
let public writeTest =
let aaa = vector [1.1;2.2]
let serializer = new DataContractJsonSerializer( typeof<MyType> )
let writer = new StreamWriter( #"c:\test.txt" )
serializer.WriteObject(writer.BaseStream, aaa)
writer.Close()
I think that F# vector type doesn't provide all the necessary support for JSON serialization (and is quite a complex type internally). Perhaps the best thing to do in your case would be to convert it to an array and serialize the array (which will also definitely generate shorter and more efficient JSON).
The conversion is quite straightforward:
let aaa = vector [1.1;2.2]
let arr = Array.ofSeq aaa // Convert vector to array
// BTW: The 'use' keyword behaves like 'using' in C#
use writer = new StreamWriter( #"c:\test.txt" )
let serializer = new DataContractJsonSerializer()
serializer.WriteObject(writer.BaseStream, aaa)
To convert the array back to vector, you can use Vector.ofSeq (which is a counterpart to Array.ofSeq used in the example above).
You can also use:
let v = vector [1.;3.];
let vArr = v.InternalValues
to get the internal array of the vector. In this way, you don't need to create a temporary array.
Types, RowVector, Matrix also has this method to get the internal arrays.