How to iterate through nested dynamic JSON file in Flutter - json

So I have two JSON files with different fields.
{
"password": { "length": 5, "reset": true},
}
"dataSettings": {
"enabled": true,
"algorithm": { "djikstra": true },
"country": { "states": {"USA": true, "Romania": false}}
}
I want to be able to use the same code to be able to print out all the nested fields and its values in the JSON.
I tried using a [JSON to Dart converter package](https://javiercbk.github.io/json_to_dart/.
However, it seems like using this would make it so I would have to hardcode all the values, since I would retrieve it by doing
item.dataSettings.country.states.USA
which is a hardcoded method of doing it. Instead I want a way to loop through all the nested values and print it out without having to write it out myself.

You can use ´dart:convert´ to convert the JSON string into an object of type Map<String, dynamic>:
import 'dart:convert';
final jsonData = "{'someKey' : 'someValue'}";
final parsedJson = jsonDecode(jsonData);
You can then iterate over this dict like any other:
parsedJson.forEach((key, value) {
// ... Do something with the key and value
));
For your use case of listing all keys and values, a recursive implementation might be most easy to implement:
void printMapContent(Map<String, dynamic> map) {
parsedJson.forEach((key, value) {
print("Key: $key");
if (value is String) {
print("Value: $value");
} else if (value is Map<String, dynamic>) {
// Recursive call
printMapContent(value);
}
));
}
But be aware that this type of recursive JSON parsing is generally not recommendable, because it is very unstructured and prone to errors. You should know what your data structure coming from the backend looks like and parse this data into well-structured objects.
There you can also perform input validation and verify the data is reasonable.
You can read up on the topic of "JSON parsing in dart", e.g. in this blog article.

Related

How to Import JSON data to typescript Map<K, V> type?

I am trying to import json data that might include present or absent mappings within one of the properties, and figured the correct data type to represent this was Map<string, number>, but I'm getting an error when I try to do this.
My JSON file, data.json, looks like this:
{
"datas": [
{
"name":"test1",
"config":"origin",
"entries": {
"red":1,
"green":2
}
}
,
{
"name":"test2",
"config":"remote",
"entries": {
"red":1,
"blue":3
}
}
,
{
"name":"test3",
"entries": {
"red":1,
"blue":3,
"purple":3
}
}
]
}
My typescript code, Data.ts, which attempts to read it looks like this:
import data from './data.json';
export class Data {
public name:string;
public config:string;
public entries:Map<string, number>;
constructor(
name:string,
entries:Map<string, number>,
config?:string
) {
this.name = name;
this.entries = entries;
this.config = config ?? "origin";
}
}
export class DataManager {
public datas:Data[] = data.datas;
}
But that last line, public datas:Data[] = data.datas;, is throwing an error.
Is there a proper way to import data like this?
The goal, ultimately, is to achieve three things:
Any time entries is present, it should receive some validation that it only contains properties of type number; what those properties are is unknown to the programmer, but will be relevant to the end-user.
If config is absent in the JSON file, the construction of Data objects should supply a default value (here, it's "origin")
This assignment of the data should occur with as little boilerplate code as possible. If, down the line Data is updated to have a new property (and Data.json might or might not receive updates to its data to correspond), I don't want to have to change how DataManager.data receives the values
Is this possible, and if so, what's the correct way to write code that will do this?
The lightest weight approach to this would not be to create or use classes for your data. You can instead use plain JavaScript objects, and just describe their types strongly enough for your use cases. So instead of a Data class, you can have an interface, and instead of using instances of the Map class with string-valued keys, you can just use a plain object with a string index signature to represent the type of data you already have:
interface Data {
name: string;
config: string;
entries: { [k: string]: number }
}
To make a valid Data, you don't need to use new anywhere; just make an object literal with name, config, and entries properties of the right types. The entries property is { [k: string]: number }, which means that you don't know or care what the keys are (other than the fact that they are strings as opposed to symbols), but the property values at those keys should be numbers.
Armed with that definition, let's convert data.datas to Data[] in a way that meets your three criteria:
const datas: Data[] = data.datas.map(d => ({
config: "origin", // any default values you want
...d, // the object
entries: onlyNumberValues(d.entries ?? {}) // filter out non-numeric entries
}));
function onlyNumberValues(x: { [k: string]: unknown }): { [k: string]: number } {
return Object.fromEntries(
Object.entries(x).filter(
(kv): kv is [string, number] => typeof kv[1] === "number"
)
);
}
The above sets the entries property to be a filtered version of the entries property in the incoming data, if it exists. (If entries does not exist, we use an empty object {}). The filter function is onlyNumberValues(), which breaks the object into its entries via the Object.entries() method, filters these entries with a user-defined type guard function, and packages them back into an object via the Object.fromEntries() method. The details of this function's implementation can be changed, but the idea is that you perform whatever validation/transformation you need here.
Any required property that may be absent in the JSON file should be given a default value. We do this by creating an object literal that starts with these default properties, after which we spread in the properties from the JSON object. We do this with the config property above. If the JSON object has a config property, it will overwrite the default when spread in. (At the very end we add in the entries property explicitly, to overwrite the value in the object with the filtered version).
Because we've spread the JSON object in, any properties added to the JSON object will automatically be added. Just remember to specify any defaults for these new properties, if they are required.
Let's make sure this works as desired:
console.log(datas)
/* [{
"config": "origin",
"name": "test1",
"entries": {
"red": 1,
"green": 2
}
}, {
"config": "remote",
"name": "test2",
"entries": {
"red": 1,
"blue": 3
}
}, {
"config": "origin",
"name": "test3",
"entries": {
"red": 1,
"blue": 3,
"purple": 3
}
}] */
Looks good.
Playground link to code

How do I PUT and GET JSON object of ANY format in a Spring Boot REST API using MySQL?

I need to be able to PUT a JSON data from POSTMAN, with no fixed format, store it in database(in MYSQL preferably with datatype: JSON) and then send a GET request to get the same value back. The data can be uniquely identified by an id that I'll be sending as a path variable.
I cannot define an entity class as the JSON will have no fixed format. How do I proceed?
#PutMapping("/sample/{sampleNo}")
public ResponseEntity<Object> addSampleData(
#ApiParam("Sampleto PUT") #PathVariable Long sampleNo, #RequestBody String sampleBody) {
if (sampleBody!= null) {
sampleService.save(new Sample(sampleNo, sampleBody));
return new ResponseEntity<>(HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
Not sure how to proceed with the "save" functionality so that the data is stored as a JSON object, because even if I've used datatype JSON n mySQL, GET returns a string with "\".
{
"sampleNo": 1,
"sampleData": "{\"sample\": \"test\", \"sampleNo\": \"20\"}"
}
Example: PUT request body
{
"field1": "test",
"field2": 20
}
GET response body expected
{
"field1": "test",
"field2": 20
}
P.S: I don't need to process the data, so it doesn't matter how I store it, I just need to be able to GET it back in the same format(JSON) it arrived(in the PUT req).
sampleData is returned as JSON String, so it is reasonable to include escape sequence \":
This is a valid JSON String:
String json = "{\"sample\": \"test\", \"sampleNo\": \"20\"}";
This does not compile:
String invalidJson = "{"sample": "test", "sampleNo": "20"}";
Solution:
In order to have the expected response, you have to map your MySQL JSON column into an Object.
There are several ways to achieve this, take a look at a solution here. It maps JSON column into a Map<String, Object>.

Json manipulation TypeScript Angular 2

I come from a Python Background and recently started programming using TypeScript and Angular2. I want to know how to obtain keys from a JSON object using TypeScript.
I have a response like so:
response.text()
I pass this object to a function
removeMetaData (my_data: string){
//(Various manipulation)...etc
}
i have read that I can call a json() method instead of text(). If I do that, what is the type I should use for my_data?
Then,
If my JSON looks like this:
{
"count": 100,
"next_page": "http://www.test.com/users/?page=2",
"prev_page": "http://www.test.com/users/?page=3",
"results":[
{
"username": "johnny"
},
Etc....
]
How do I parse that?
I've read I might have to use an interface but I don't understand how.
In python it's just response["next_page"] to get the key of a dictionary, then I can assign that value to a variable within the class. That is exactly what I'm trying to achieve within a component.
Thank you.
ADDITION
list() {
this.requestService.get(this.api_path)
.subscribe(
response => this.populate(response.json()),
error => this.response = error.text()
)
}
populate(payload: object) {
this.count = payload.count;
this.next = payload.next;
this.previous = payload.previous;
*payload.results => this.users = payload.results;******
}
Declare an interface which will be used as value object.
export interface IPage
{
count:number;
next_page:string;
prev_page:string;
results:Array<any>;
...
...
}
var my_data:IPage;
Now assign parsed json value to my_data and access all the properties with '.' operator i.e. my_data.count, my_data.results.
Feel free to throw any question.
If I do that, what is the type I should use for my_data?
Its just a javascript object.
As an example if you json looks like:
{
"foo": {
"bar": "bas"
}
}
Then in the parsed json (in variable someObj) the value someObj.foo.bar would be bas 🌹

How to Parse this JSON with LibGDX

"components":[
{
"class":"AssetReference",
"asset":{
"class":"TextureRegionAsset",
"relativePath":"gfx/opengraph.png"
}
},
{
"class":"Layer"
},
{
"class":"ProtoVisSprite",
"width":5,
"height":5
},
{
"class":"Transform",
"x":0.13817275,
"y":2.8430145,
"scaleX":0.2,
"scaleY":0.2
},
{
"class":"Origin"
},
{
"class":"Tint"
},
{
"class":"Renderable",
"zIndex":2
},
{
"class":"VisID",
"id":"scratch"
}
]
Im having some issues in parsing the nested asset with LibGDX. Does anyone know how to assign asset to AssetReference with the relativePath from TextureRegionAsset?
I know I could strip out the "class" handling and simple parse the JSON but I need to be able to handle this with LibGDX.
Ideally Im looking to parse the data and create a sprite from the JSON.
Thanks.
You can use JsonReader and get JsonValue for that.
JsonReader json = new JsonReader();
JsonValue base = json.parse(Gdx.files.internal("file.json"));
//print json to console
System.out.println(base);
//get the component values
JsonValue component = base.get("components");
//print class value to console
System.out.println(component.getString("class"));
//array objects in json if you would have more components
for (JsonValue component : base.get("components"))
{
System.out.println(component.getString("class"));
System.out.println(component.get("asset").getString("class");
System.out.println(component.get("asset").getString("relativePath");
}
There is actually a useful libgdx wiki page for this:
https://libgdx.com/wiki/utils/reading-and-writing-json
Apparently it seems to work fine with nested classes on its own already.
The wiki page has this example:
Json json = new Json();
Person person = json.fromJson(Person.class, text);
Using the following as text:
{
class: com.example.Person,
numbers: [
{
class: com.example.PhoneNumber,
number: "206-555-1234",
name: Home
},
{
class: com.example.PhoneNumber,
number: "425-555-4321",
name: Work
}
],
name: Nate,
age: 31
}
This is using an example class "Person" with the following properties:
ArrayList numbers
String name
int age
The String text is the result of json.toJson(person). Your resulting serialized string seems the same format, which makes me assume you're already using the Json serializer, but not the unserializer.

Grails: Parsing through JSON String using JSONArray/JSONObject

I have the below JSON string coming in as a request parameter into my grails controller.
{
"loginName":"user1",
"timesheetList":
[
{
"periodBegin":"2014/10/12",
"periodEnd":"2014/10/18",
"timesheetRows":[
{
"task":"Cleaning",
"description":"cleaning description",
"paycode":"payCode1"
},
{
"task":"painting",
"activityDescription":"painting description",
"paycode":"payCode2"
}
]
}
],
"overallStatus":"SUCCESS"
}
As you can see, the timesheetList might have multiple elements in it. In this ( above ) case, we have only one. So, I expect it to behave like an Array/List.
Then I had the below code to parse through it:
String saveJSON // This holds the above JSON string.
def jsonObject = grails.converters.JSON.parse(saveJSON) // No problem here. Returns a JSONObject. I checked the class type.
def jsonArray = jsonArray.timesheetList // No problem here. Returns a JSONArray. I checked the class type.
println "*** Size of jsonArray1: " + jsonArray1.size() // Returns size 1. It seemed fine as the above JSON string had only one timesheet in timesheetList
def timesheet1 = jsonArray[1] // This throws the JSONException, JSONArray[1] not found. I tried jsonArray.getJSONObject(1) and that throws the same exception.
Basically, I am looking to seamlessly iterate through the JSON string now. Any help?
1st off to simplify your code, use request.JSON. Then request.JSON.list[ 0 ] should be working