how to parse dynamic complex json to dart object or model - json

{
"transactionId": "11f8ecc05273e35a4eb2dc1a",
"type": "REQUEST_FOR_HH_INTERVIEW",
"answers": {
"selectProvinceDistrictCommuneVillage": {
"value": "01020706"
},
"positionOfOfficial": {
"value": "Province Officer"
},
"enterKhmerName": {
"value": "សុខ"
},
"selectSex": {
"value": "MALE"
},
"dob": {
"value": "1994-06-15T03:27:47.409Z"
},
"areYouMarried": {
"value": "YES"
},
"scanSpousesID": {
"value": "435465"
},
"enterSpousesKhmerName": {
"value": "នារី"
},
"selectSexSpouse": {
"value": "FEMALE"
},
"dobSpouse": {
"value": "1996-08-15T03:27:47.409"
},
"numberOfMales": {
"value": "4"
},
"numberOfFemales": {
"value": "5"
},
"selectReasonForRequesting": {
"value": [
"NATURAL_DISASTER"
]
}
}
}
So this is the JSON I need to parse into the dart model. The problem I am having with this structure is that map inside answers are all dynamic. Also, the number of the maps inside answers is not always the same. For example, the next JSON response can be.
{
"transactionId": "11f8ecc05273e35a4eb2dc1a",
"type": "REQUEST_FOR_HH_INTERVIEW",
"answers": {
"selectCode": {
"value": "01020706"
},
"selectRoomValue": {
"value": "1996-08-15T03:27:47.409"
},
"numberOfFamilyMembers": {
"value": "4"
},
"selectFoods": {
"value": [
"Piza",
"Burger"
]
}
}
}
which is different from the first response. I need to make a dart model that parses both responses.

This is relatively easy to do by using a "sub-model" of Answers which would be stored within the assumed InterviewRequest model.
For example:
class InterviewRequest {
final Answers answers;
final String transactionId;
factory InterviewRequest.fromJson(Map<String, dynamic> json) {
return InterviewRequest(
answers: Answers.fromJson(json['answers']),
transactionId: json['transactionId'] as String,
);
}
}
class Answers {
final List<Answer> answers;
factory Answers.fromJson(Map<String, dynamic> json) {
List answers = [];
for (String question in json.keys)
answers.add(Answer(question, json[key]));
return Answers(answers);
}
}

Related

Flutter: parsing json response that has multiple keys

I'm trying to get the values of these keys from this json response:
{
"pro": {
"groups": [
"1": {
"name": "Base",
"fields": [
{
"id": 3,
"value": {
"raw": "Name",
}
},
{
"id": 4,
"value": {
"raw": "avatar",
}
},
]
},
"2": {
"name": "Base",
"fields": [
{
"id": 6,
"value": {
"raw": "Name",
}
},
{
"id": 7,
"value": {
"raw": "avatar",
}
},
]
}
]
}
}
I could get the values "name": "Base"
json['pro']['groups']["1"]['name'],
But I can't get the values of key "raw".
How can I get the values of key "raw"?
The values of fields are a list, so you will get a list of raw values:
List<String> raw = json['pro']['groups']['1']['fields'].map((v) => v['value']['raw'];
Also, it seems like groups is an array but as an object? then you can do something like this:
List<String> raw = [];
Map<String, dynamic> groups = json['pro']['groups'];
for (var key in groups.keys) {
raw.add(groups[key]['fields'].map((v) => v['value']['raw']);
}
or
List<String> raw = groups.keys.map((key) => groups[key]['fields'].map((v) => v['value']['raw']);
I haven't tested the code, but hopefully, it works as expected :)
first thing first. your json is invalid.
try to paste your json here it will show why is your json is invalid
after you fix the json the new structure json will be looked like this
{
"pro": {
"groups": [
{
"name": "Base",
"fields": [
{
"id": 3,
"value": {
"raw": "Name",
}
},
{
"id": 4,
"value": {
"raw": "avatar",
}
},
]
},
{
"name": "Base",
"fields": [
{
"id": 6,
"value": {
"raw": "Name",
}
},
{
"id": 7,
"value": {
"raw": "avatar",
}
},
]
}
]
}
}
and then in order to grab the value of raw, you have to parse the json first using jsonDecode(), then you can use something like this:
Map<String, dynamic> groupOne = json['pro']['groups'][0];
Map<String, dynamic> groupOneFieldOne = groupOne['fields'][0];
print(groupOneFieldOne['value']['raw']);
but that's just an example. if you want to access them easily you can use .map() like this:
List<Map<String, dynamic>> groups = json['pro']['groups'];
groups.map(
(Map<String, dynamic> group) => (group['fields'] as List<dynamic>).map(
(dynamic field) => field['value']['raw'],
),
);
that's it! if you want to ask anything just put a comment ;)
you can copy and paste on dartpad
List<Map<String, dynamic>> groups = json['pro']['groups'];
print(groups.map(
(Map<String, dynamic> group) => (group['fields'] as List<dynamic>).map(
(dynamic field) => field['value']['raw'],
),
));
}
Map<String, dynamic> json = {
"pro": {
"groups": [
{
"name": "Base",
"fields": [
{
"id": 3,
"value": {
"raw": "Name",
}
},
{
"id": 4,
"value": {
"raw": "avatar",
}
},
]
},
{
"name": "Base",
"fields": [
{
"id": 6,
"value": {
"raw": "Name",
}
},
{
"id": 7,
"value": {
"raw": "avatar",
}
},
]
}
]
}
};

How to parse an array of json in scala play framework?

I have an array of json objects like this
[
{
"events": [
{
"type": "message",
"attributes": [
{
"key": "action",
"value": "withdraw_reward"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "module",
"value": "distribution"
},
{
"key": "sender",
"value": "bob"
}
]
},
{
"type": "credit",
"attributes": [
{
"key": "recipient",
"value": "ross"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "amount",
"value": "100"
}
]
},
{
"type": "rewards",
"attributes": [
{
"key": "amount",
"value": "100"
},
{
"key": "validator",
"value": "sarah"
}
]
}
]
},
{
"events": [
{
"type": "message",
"attributes": [
{
"key": "action",
"value": "withdraw_reward"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "module",
"value": "distribution"
},
{
"key": "sender",
"value": "bob"
}
]
},
{
"type": "credit",
"attributes": [
{
"key": "recipient",
"value": "ross"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "amount",
"value": "100"
}
]
},
{
"type": "rewards",
"attributes": [
{
"key": "amount",
"value": "200"
},
{
"key": "validator",
"value": "Ryan"
}
]
}
]
}
]
How to traverse through the types, check if it's type equals to rewards and then go through the attributes and verify if the validator equals to sarah and fetch the value of the key amount? Pretty new to scala and play framework. Any help would be great. Thanks
You could parse your JSON into a structure of case classes for easier handling and then extract the wanted field like so:
val json =
"""[
{"events":[
{
"type":"message","attributes":[
{"key":"action","value":"withdraw_reward"},
{"key":"sender","value":"bob"},
{"key":"module","value":"distribution"},
{"key":"sender","value":"bob"}
]},
{
"type":"credit","attributes":[
{"key":"recipient","value":"ross"},
{"key":"sender","value":"bob"},
{"key":"amount","value":"100"}
]},
{
"type":"rewards","attributes":[
{"key":"amount","value":"100"},
{"key":"validator","value":"sara"}
]}
]
},
{"events":[
{
"type":"message","attributes":[
{"key":"action","value":"withdraw_reward"},
{"key":"sender","value":"bob"},
{"key":"module","value":"distribution"},
{"key":"sender","value":"bob"}
]},
{
"type":"credit","attributes":[
{"key":"recipient","value":"ross"},
{"key":"sender","value":"bob"},
{"key":"amount","value":"100"}
]},
{
"type":"rewards","attributes":[
{"key":"amount","value":"200"},
{"key":"validator","value":"Ryan"}
]}
]
}
]
"""
case class EventWrapper(events: Seq[Event])
case class KeyValue(key: String, value: String)
case class Event(`type`: String, attributes: Seq[KeyValue])
import play.api.libs.json._
implicit val kvReads: Reads[KeyValue] = Json.reads[KeyValue]
implicit val eventReads: Reads[Event] = Json.reads[Event]
implicit val eventWrapperReads: Reads[EventWrapper] = Json.reads[EventWrapper]
val rewardAmountsValidatedBySara = Json
.parse(json)
.as[Seq[EventWrapper]]
.flatMap {
_.events.collect {
case Event(t, attributes) if t == "rewards" && attributes.contains(KeyValue("validator", "sara")) =>
attributes.collect {
case KeyValue("amount", value) => value
}
}.flatten
}
val amount = rewardAmountsValidatedBySara.head
For your example, rewardAmountsValidatedBySara would yield a List of Strings containing only the String "100". Which you could retrieve (potentially unsafe) with .head as shown above.
Normally you would not do this, as it could throw an exception on an empty List, so it would be better to use .headOption which returns an Option which you can then handle safely.
Note that the implicit Reads are Macros, which automatically translate into Code, that instructs the Play Json Framework how to read the JsValue into the defined case classes, see the documentation for more info.

Cleaner way of iterating nested JSON in ruby

I was wondering if there is any 'cleaner' way of looping through nested JSON in ruby?
This is my JSON object:
{
"data": [
{
"file": "test/test_project_js/jquery.js",
"results": [
{
"vulnerabilities": [
{
"severity": "high"
},
{
"severity": "medium"
},
{
"severity": "none"
},
{
"severity": "high"
}
]
}
]
},
{
"file": "test/test_project_js/jquery.js",
"results": [
{
"vulnerabilities": [
{
"severity": "none"
},
{
"severity": "none"
},
{
"severity": "none"
},
{
"severity": "high"
}
]
}
]
}
]
}
I want to extract severity under each vulnerability present inside each results[] which is under data[]
Current code approach is
severity_arr = raw['data'].each do |data|
data['results'].each do |result|
result['vulnerabilities'].map {|vulnerability| vulnerability['severity']}
end
end
You can use flat_map and dig:
data[:data].flat_map { |datum| datum.dig(:results, 0, :vulnerabilities) }
# [{:severity=>"high"}, {:severity=>"medium"}, {:severity=>"none"}, {:severity=>"high"}, {:severity=>"none"}, {:severity=>"none"}, {:severity=>"none"}, {:severity=>"high"}]
What's maybe not convenient, is that data.results holds an array with a single hash. Maybe a hash is enough for that.

Elasticsearch NEST 2.x Field Names

I am upgrading to NEST 2.3.0 and trying to rewrite all queries that were originally written for NEST 1.x.
I am using the Couchbase transport plugin that pushes data from Couchbase to Elasticsearch.
POCO
public class Park
{
public Park()
{
}
public bool IsPublic { get; set; }
}
Mapping is like this
"mappings": {
"park": {
"_source": {
"includes": [
"doc.*"
],
"excludes": [
"meta.*"
]
},
"properties": {
"meta": {
"properties": {
"rev": {
"type": "string"
},
"flags": {
"type": "long"
},
"expiration": {
"type": "long"
},
"id": {
"type": "string",
"index": "not_analyzed"
}
}
},
"doc": {
"properties": {
"isPublic": {
"type": "boolean"
}
}
}
}
}
}
Sample document in elasticsearch
{
"_index": "parkindex-local-01",
"_type": "park",
"_id": "park_GUID",
"_source": {
"meta": {
"expiration": 0,
"flags": 33554433,
"id": "park_GUID",
"rev": "1-1441a2c278100bc00000000002000001"
},
"doc": {
"isPublic": true,
"id": "park_GUID"
}
}
}
My query in NEST
var termQuery = Query<Park>.Term(p => p.IsPublic, true);
ISearchResponse<T> searchResponse = this.client.Search<T>(s => s.Index("parkindex-local-01")
.Take(size)
.Source(false)
.Query(q => termQuery));
This query goes to Elasticsearch as below
{
"size": 10,
"_source": {
"exclude": [
"*"
]
},
"query": {
"term": {
"isPublic": {
"value": "true"
}
}
}
}
It doesn't retrieve any data, it will work only if I prefix the field name with "doc." so query becomes as below
{
"size": 10,
"_source": {
"exclude": [
"*"
]
},
"query": {
"term": {
"doc.isPublic": {
"value": "true"
}
}
}
}
How do I write the query above in NEST so it can properly interpret the field names, I tried using Nested Query with Path set to "doc", but that gave an error saying field is not of a nested type.
Do I need to change my mapping?
This all used to work in Elasticsearch 1.x and NEST 1.x, I guess this has to do with breaking changes to field names constraints.
Fields can no longer be referenced by shortnames in Elasticsearch 2.0.
isPublic is a property of the doc field, which is mapped as an object type, so referencing by the full path to the property is the correct thing to do.
NEST 2.x has some ways to help with field inference, an example
public class Park
{
public Doc Doc { get; set;}
}
public class Doc
{
public bool IsPublic { get; set;}
}
var termQuery = Query<Park>.Term(p => p.Doc.IsPublic, true);
client.Search<Park>(s => s.Index("parkindex-local-01")
.Take(10)
.Source(false)
.Query(q => termQuery));
results in
{
"size": 10,
"_source": {
"exclude": [
"*"
]
},
"query": {
"term": {
"doc.isPublic": {
"value": true
}
}
}
}
You may also want to take a look at the automapping documentation.

How to find length of JSON?

This is my JSON array and how i can find JSON array length.
{"quizlist":[
{
"question":"lion.png",
"option":"lion"
},
{
"question":"tiger.png",
"option":"tiger"
},
{
"question":"cheetah.png",
"option":"cheetah"
},
]}
In your case, it looks like a JS Object already.
So, simply use
var jsObj = {
"quizlist": [{
"question": "lion.png",
"option": "lion"
}, {
"question": "tiger.png",
"option": "tiger"
}, {
"question": "cheetah.png",
"option": "cheetah"
}, ]
}
alert(jsObj.quizlist.length)