I try to convert a json response to a list of persons.
This solution works but maybe there is a better solution to parse it.
(I can't change the response structure, but i can change the person if necessary)
Json Response:
{
"name1": {
"address": "abc",
"city": "xy"
},
"name2": {
"address": "abcdef",
"city": "xyzzzz"
}
}
My Person:
class Person{
name:string;
city:string;
address:string;
constructor(name: string, city: string, address: string) {
this.name = name;
this.city = city;
this.address = address;
}
}
My example implementation:
const value = JSON.parse(data);
const arr:Person[] = [];
for (var key in value) {
if (value.hasOwnProperty(key)) {
arr.push(new Person(key, value[key].city, value[key].address));
}
}
JSON.parse is dangerous, you must do with try catch
let value
try {
value = JSON.parse(data);
} catch (err) {
value = {};
}
const arr:Person[] = Object.keys(value)
.map(key => new Person(key, value[key].city, value[key].address));
Related
I have the below json data, wherein I want to access the values inside the feeling_percentage.
{
"status": "200",
"data": {
"feeling_percentage": {
"Happy": "0",
"Sad": "0",
"Energetic": "0",
"Calm": "0",
"Angry": "0",
"Bored": "0"
},
}
}
I am able to fetch it using the below API code
Future<List<Data>> makePostRequest() async {
List<Data> list = [];
final uri = Uri.parse('<api>');
final headers = {'Content-Type': 'application/json', 'X-Api-Key':'<api_key>'};
Map<String, dynamic> body = {'user_id': 3206161992, 'feeling_date': '15-04-2022'};
String jsonBody = json.encode(body);
final encoding = Encoding.getByName('utf-8');
Response response = await post(
uri,
headers: headers,
body: jsonBody,
encoding: encoding,
);
int statusCode = response.statusCode;
String responseBody = response.body;
print('response body'+ responseBody);
}
After reading few articles, still not able to figure out how do I access the percentage of happy, sad inside the feeling_percentage.
I have created the model as
class Data{
FeelingPercentage feelingPercentage;
Data(
{required this.feelingPercentage});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
feelingPercentage: FeelingPercentage.fromJson(json["data"]),
);
}
}
class FeelingPercentage {
String? happy;
String? sad;
String? energetic;
String? calm;
String? angry;
String? bored;
FeelingPercentage({this.happy, this.sad, this.energetic, this.calm, this.angry, this.bored});
factory FeelingPercentage.fromJson(Map<String, dynamic> json) {
return FeelingPercentage(
happy: json["happy"] as String,
sad: json["sad"] as String,
energetic: json["energetic"] as String,
calm: json["calm"] as String,
angry: json["angry"] as String,
bored: json["bored"] as String,
);
}
}
Another way:
import 'package:fast_json/fast_json_selector.dart' as parser;
void main() {
final path = '{}.data.{}.feeling_percentage';
final level = path.split('.').length;
void select(parser.JsonSelectorEvent event) {
final levels = event.levels;
if (levels.length == level && levels.join('.') == path) {
print(event.lastValue);
event.lastValue = null;
}
}
parser.parse(_source, select: select);
}
const _source = '''
{
"status": "200",
"data": {
"feeling_percentage": {
"Happy": "0",
"Sad": "0",
"Energetic": "0",
"Calm": "0",
"Angry": "0",
"Bored": "0"
}
}
}''';
Output:
{Happy: 0, Sad: 0, Energetic: 0, Calm: 0, Angry: 0, Bored: 0}
You can use this website to convert your JSON object to a dart class.
it automatically creates the fromJson function, which can be used to pass JSON and receive the Dart objects.
Change this line in your model feelingPercentage: FeelingPercentage.fromJson(json["data"]), to feelingPercentage: FeelingPercentage.fromJson(json["data"]["feeling_percentage"]),
This will fix your issue.
You can do a JSON decode that will will result in a map, and then do the assigned like you are doing on your from Json factory, but as another constructor instead:
Class
Todo.fromMap(Map map) :
this.title = map['title'],
this.completed = map['completed'];
In use
Todo.fromMap(json.decode(item))
First decode response.body, then create FeelingPercentage object from json["data"]["feeling_percentage"] map.
Future<FeelingPercentage> makePostRequest() async {
...
final json = json.decode(response.body);
return FeelingPercentage.fromJson(json["data"]["feeling_percentage"])
}
class FeelingPercentage {
String? happy;
String? sad;
String? energetic;
String? calm;
String? angry;
String? bored;
FeelingPercentage({this.happy, this.sad, this.energetic, this.calm, this.angry, this.bored});
factory FeelingPercentage.fromJson(Map<String, dynamic> json) {
return FeelingPercentage(
happy: json["Happy"] as String,
sad: json["Sad"] as String,
energetic: json["Energetic"] as String,
calm: json["Calm"] as String,
angry: json["Angry"] as String,
bored: json["Bored"] as String,
);
}
}
I actually learning SwiftUi, and at the moment i don't get into my problem.
I try to load Json Data from my local Json File !
I have searched many many many Post but nothing really helped me, that is why i have chosen to make this post.
I show you how i possible tried it.
Path: Data/test.json
[
{
"id": 1,
"name": "",
"trade_name": "",
"short_name": "",
"test": "",
"test1": "",
"test2": "",
"test3": "",
"test4": "",
"test5": "",
"test6": "",
"test7": "",
"test8": [],
"test9": "",
"test10": "",
"test11": "",
"test12": ""
},
]
Path: App/DataLoader.swift
import Foundation
public class DataLoader {
#Published var contentData = [JSONData]()
init(){
load()
sort()
}
func load(){
if let fileLocation = Bundle.main.url(forResource: "test", withExtension: "json"){
do {
let data = try Data(contentsOf: fileLocation)
let jsonDecoder = JSONDecoder()
let dataFromJson = try jsonDecoder.decode([JSONData].self, from: data)
self.contentData = dataFromJson
} catch {
print(error)
}
}
}
func sort(){
self.contentData = self.contentData.sorted(by: { $0.id < $1.id})
}
}
Here in this File i add the variables.
Path: App/JsonData.swift
import Foundation
struct JSONData: Codable {
var id: Int
var name: String
var trade_name: String
var short_name: String
var test: String
var test1: String
var test2: [String:String]
var test3: [String:String]
var test4: String
var test5: String
var test6: [String:String]
var test7: String
var test8: [String:String]
var test9: String
var test10: String
var test11: String
var test12: String
}
I didn't set the Display yet but i want it in a List.
In my ContentView.swift File i would load the Data from that Json File !
Path: App/ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
Home()
.navigationTitle("Test Interface")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home : View {
let data = DataLoader().contentData
var body: some View {
data[IndexPath.row].id
}
}
When i did it like that, it shows some Errors.
Return type of property 'body' requires that 'Int' conform to 'View'
Instance member 'row' cannot be used on type 'IndexPath'; did you mean to use a value of this type instead?
Regards
CreatingBytes
SwiftUI is not UIKit, there are no index paths.
The body property of a View must return some View, nothing else.
First of all adopt Identifiable
struct JSONData: Codable, Identifiable { ...
Then adopt ObservableObject in DataLoader
public class DataLoader : ObservableObject {
You have to observe the data in the root view, replace ContentView with
struct ContentView: View {
#StateObject var data = DataLoader()
var body: some View {
NavigationView {
Home(data: data)
.navigationTitle("Test Interface")
.navigationBarTitleDisplayMode(.inline)
}
}
}
In Home hand over data and use ForEach
struct Home : View {
#ObservedObject var data : DataLoader
var body: some View {
VStack() {
ForEach(data.contentData) { item in
HStack {
Text(item.name)
Spacer()
Text("\(item.id)")
}
}
}
}
}
Alternatively replace ForEach with List to get a table view
I highly recommend to watch the videos of WWDC 2019 and 2020 about SwiftUI
I'm looking for a way to convert my typescript class with dictionary to a JSON object without the brackets.
this is my class
export class MediaTagRequest {
tags: Map<string, string>;
constructor(tags: Map<string, string>) {
this.tags = tags;
}
}
My instantiation
let tags = new Map<string, string>();
tags.set("city", "Karachi");
let mediatagRequest = new MediaTagRequest(tags);
const headers = { 'content-type': 'application/json'}
const body = JSON.stringify(Object.keys(mediatagRequest.tags.entries()));
My current output:
[["city","Karachi"]]
My desired output:
{
"tags": {
"city": "Karachi"
}
}
Can someone help me please, thank you.
You can convert a map to an object directly by using Map#entries and Object.fromEntries.
Here is an example:
const m = new Map();
m.set("foo", "hello");
m.set("bar", "world");
const obj = Object.fromEntries(m.entries());
console.log(obj);
You can further leverage the replacer parameter of JSON.stringify to directly do this when converting an entire object:
function mapReplacer(key: string | number | Symbol, value: any) {
if (value instanceof Map) {
return Object.fromEntries(value.entries());
}
return value;
}
class MediaTagRequest {
tags: Map<string, string>;
constructor(tags: Map<string, string>) {
this.tags = tags;
}
}
let tags = new Map<string, string>();
tags.set("city", "Karachi");
let mediatagRequest = new MediaTagRequest(tags);
console.log(JSON.stringify(mediatagRequest, mapReplacer))
Playground Link
JavaScript demo:
function mapReplacer(key, value) {
if (value instanceof Map) {
return Object.fromEntries(value.entries());
}
return value;
}
class MediaTagRequest {
constructor(tags) {
this.tags = tags;
}
}
let tags = new Map();
tags.set("city", "Karachi");
let mediatagRequest = new MediaTagRequest(tags);
console.log(JSON.stringify(mediatagRequest, mapReplacer))
You can use any of this to create object and then create response body using it
Option 1
let jsonObject = {};
tags.forEach((value, key) => {
jsonObject[key] = value;
});
Option 2
let jsonObject = {};
for (let entry of tags.entries()) {
jsonObject[entry[0]] = entry[1];
}
Option 3
let jsonObject = {};
for (let key of tags.keys()) {
jsonObject[key] = value;
}
creating response body
const body = JSON.stringify({
tags: jsonObject
});
I'm trying to retrieve data from a local JSON file like this:
[{
"name": "John",
"lastname": "Doe",
"age": "30"
},
{
"name": "Jane",
"lastname": "Doe",
"age": "20"
}
,
{
"name": "Baby",
"lastname": "Doe",
"age": "3"
}]
The user, using a datapicker can select name and/or lastname
import SwiftUI
struct ContentView : View {
var names = ["John", "Jane", "Baby"]
var lastnames = ["Doe", "Boe"]
#State private var selectedNameItem = 0
#State private var selectedLastNameItem = 0
var body: some View {
VStack {
Picker(selection: $selectedNameItem, label: Text("Names:")) {
ForEach(0 ..< names.count) {
Text(self.names[$0]).tag($0)
}
}
Text("Your choice: ")
+ Text("\(names[selectedNameItem])")
}
VStack {
Picker(selection: $selectedLastNameItem, label: Text("LastName:")) {
ForEach(0 ..< lastnames.count) {
Text(self.lastnames[$0]).tag($0)
}
}
Text("Your choice: ")
+ Text("\(lastnames[selectedLastNameItem])")
}
}
}
Once selected the name/lastyname (as parameter) I want to show a text that said for example: "John Doe is 30 years old"
How I can read the data from JSON and return exactly the age of the user selected without list all the elements?
Thanks a lot,
Fabrizio
To start, I recommend making a struct to represent your JSON structure. Try the following:
struct Person: Codable, Hashable {
let name: String
let lastname: String
let age: String
}
typealias People = [Person]
I usually make a typealias to handle the array version of my data. Once you have defined your data structure to match your JSON, I usually extend my data to make loading from JSON easy.
extension Person {
static func load(fromJson json: URL) -> People {
guard let data = try? Data(contentsOf: json) else {
preconditionFailure("Unable to read data from URL")
}
let jsonDecoder = JSONDecoder()
var people = People()
do {
people = try jsonDecoder.decode(People.self, from: data)
} catch {
print(error)
}
return people
}
}
There are more generic ways to do this to support a wider swath of models, but for your example, this is quick and easy.
Now that you have an easy way to work with your model, an easy solution to what you have in mind could be done by extending the Array type like so:
extension Array where Element == Person {
func retrievePeople(byName name: String) -> People {
return self.filter { $0.name == name }
}
func retrievePeople(byLastName lastname: String) -> People {
return self.filter { $0.lastname == lastname }
}
func retrievePeople(byAge age: String) -> People {
return self.filter { $0.age == age }
}
}
This will allow you to query the entire range of objects by any of the elements and in turn, return the array of matches. If you're certain that there's only one return, you could use the following code to get the first element:
// Let's assume your array of people is stored in this variable
var myPeople: People
if let person = myPeople.retrievePeople(byName: "John").first {
// Do the things you want with this person object here
}
The nice thing about this style of loading/working with data is that it's easy to generate quickly and it will support returning 0 objects, 1 object, and multiple objects. This will also allow you to move the model over to use the features of SwiftUI/Combine (which is what it looks like you're hoping to do above).
I am trying to decode some JSON. Here is an example of the JSON:
[
{
"type": "departure",
"status": "landed",
"departure": {
"iataCode": "JFK",
"icaoCode": "KJFK",
"scheduledTime": "2017-12-11T01:06:00.000",
"estimatedRunway": "2017-12-11T02:07:00.000",
"actualRunway": "2017-12-11T02:07:00.000" },
"arrival": {
"iataCode": "CVG",
"icaoCode": "KCVG",
"estimatedRunway": "2017-12-11T03:38:00.000",
"actualRunway": "2017-12-11T03:38:00.000"
},
"airline": {
"name": "Atlas Air",
"iataCode": "5Y",
"icaoCode": "GTI"
},
"flight": {
"number": "302",
"iataNumber": "5Y302",
"icaoNumber": "GTI302"
}
},
{
//Same keys as above.
},
//Etc.
]
It begins as an unkeyed array. This is then follewed by JSON containers that are also unkeyed. I am having trouble breaking it apart using this code:
struct Dataset: Decodable {
var data: [FlightData]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
print(container)
data = [try container.decode(FlightData.self)]
}
struct FlightData: Decodable {
var type: String //Arrival or Departure
var status: String //Flight Status
var departure: Departure
var arrival: Arrival
var airline: Airline
var flight: Flight
struct Departure: Decodable {
var iataCode: String
var icaoCode: String
var terminal: String
var gate: String
var scheduledTime: String
var estimatedTime: String
var actualTime: String
var estimatedRunway: String
var actualRunway: String
}
struct Arrival: Decodable {
var iataCode: String
var icaoCode: String
var terminal: String
var gate: String
var baggage: String
var scheduledTime: String
var estimatedTime: String
var actualTime: String
var estimatedRunway: String
var actualRunway: String
}
struct Airline: Decodable {
var name: String
var iataCode: String
var icaoCode: String
}
struct Flight: Decodable {
var number: String
var iataNumber: String
var icaoNumber: String
}
}
}
Im new to JSON and Swift Decodable so I am a bit confused what I am doing wrong?
Does anyone know how I can fix my issue?
Right now I am getting the warning that it is expecting an array but it is finding a dictionary. Therefore, I think I have successfully gotten past the first unkeyed container but I cant get into the rest of it.
Remove your init method and then do
let decoder = JSONDecoder()
do {
data = try decoder.decode([FlightData].self, from: data)
} catch {
print(error)
}