Decoding json from google firebase rest api using Dart, not Flutter - json

I can retrieve a list of documents from a collection in a Cloud Firestore instance, in Firebase. The response contains the most verbose json I have ever seen. Here is a taste, ...
{
documents: [
{
name: projects/myprojectId/databases/(default)/documents/mycollection/0HC2spBFxEMNUc8VQLFg,
fields: {
name: {
stringValue: Jim's Bait Shop},
taxId: {
stringValue:
},
mailingAddress: {
mapValue: {
fields: {
streetAddress1: {
stringValue:
}
},
streetAddress2: {
stringValue:
},
state: {
stringValue: NC
},
city: {
stringValue: Boone
},
zipCode: {
stringValue:
}
}
}
}
},
createTime: 2020-08-31T19
:
54: 28.643464Z,
updateTime: 2020-09-01T02
:
35: 08.203028Z
},
{ ...
When trying to use jsonDecode, in dart:convert, it fails to de-serialize the json response into a collection of Dart objects.
'_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
And if I use cUrl instead of Dart, the json response looks just as verbose.
I'm using the FirebaseClient in "package:firebase/firebase_io.dart" to authenticate and read the collection.
I tried to build a "reviver" function but jsonDecode would not accept it so I'm not sure how I messed that up.
Anyway, I'm not seeing much guidance in the documentation on how to marshal this verbose json response into Dart objects. I suspect this server-side Dart is somewhat new territory. I want to avoid packages that require Flutter because I'm using a prebuilt docker image, with the Dart runtime preinstalled, on Google Cloud Run. (Truthfully, I've already tried a few Flutter packages for Firestore and a Flutter docker image.) I'll take any suggestions you have.
Below is the file I've been using for testing.
import 'package:firebase/firebase_io.dart';
import 'credentials.dart'; // borrowed from a SO post
import 'dart:convert';
const base = 'https://firestore.googleapis.com/v1/projects/';
void main() async {
// get private key...
final credential = await Credentials.fetch(); // string
final fbClient = FirebaseClient(credential);
final path = base + 'my_project_id/databases/(default)/documents/my_collection'
'?mask.fieldPaths=name&mask.fieldPaths=taxId&mask.fieldPaths=mailingAddress&orderBy=orgId';
final response = await fbClient.get(path);
print(response);
final orgs = jsonDecode(response); // unhandled exception
fbClient.close();
}
I think I might need to switch to a more sophisticated json deserializer package, and annotate my model classes to explicitly map this gnarly json to specific Dart class properties. But I have not yet seen a Dart package that supports such capabilities.
I have tried to use "json_serializable: 3.4.1" but failed to get code generation to work.
An online json validator is saying the response is malformed due to an apostrophe but can I trust that? Doubt I can escape special chars.

The error message says that response is not a String, it's a Map.
That means that Firebase has already parsed the JSON for you and returns the parsed structure.
You don't need to use jsonDecode, just final orgs = response;.

The solution was to stop using FirebaseClient, because it was not wrapping the name-value pairs in double quotation marks. Just use normal http instead.
import 'package:http/http.dart' as http;
const base = 'https://firestore.googleapis.com/v1/projects/';
void main() async {
//
final uri = base + 'myproject/databases/(default)/documents/mycollection' +
'?mask.fieldPaths=name&mask.fieldPaths=taxId&mask.fieldPaths=mailingAddress&orderBy=orgId&alt=json';
var response = await http.get(uri);
// print(response.body);
var json = jsonDecode(response.body)['documents'] as List;
List l = json.map((o) => MyModelClass.fromJson(o)).toList();
https://pub.dev/packages/http

Related

Android Kotlin Json Data parsing MVVM

I am new to android and currently I am learning kotlin I am using MVVM to get the remote data using Retrofit but I am not sure how to parse or implement the data got from JSON I am using json converter plugin to convert the json to data class. Below are my implementation i am not sure how to call or get the DATA from MyPostItems
This is the link I am using to get the JSON -> https://jsonplaceholder.typicode.com/posts -> As the arraylist does not have any name
I have two Data Class generated by JSON Coverter
class MyPost : ArrayList<MyPostItem>() -> you see here it does not even say Data Class and this is the base class i am calling using retrofit
#Entity(tableName = "Posts")
data class MyPostItem(
val body: String,
#PrimaryKey(autoGenerate = true)
val id: Int? = null,
val title: String,
val userId: Int
)
Here is my MVVM call from main Activity
viewModel.postData.observe(this, Observer { response->
when(response) {
is NetworkResource.Success -> {
binding.mainActivityProgressBar.visibility = View.GONE
response.data?.let { postResponse->
postAdapter.differ.submitList(postResponse) -> here i am getting the response but its the whole list not in the form of MyPostItems
}
}
is NetworkResource.Error -> {
binding.mainActivityProgressBar.visibility = View.GONE
response.message?.let { message ->
Log.e(TAG, "An error occured: $message")
}
}
is NetworkResource.Loading -> {
binding.mainActivityProgressBar.visibility = View.VISIBLE
}
}
})
}
P.S If anyone has resources or any study material on how to work with nested JSON object the link or reference to that would be very much apricated

How to Read JSON file in Dart console app?

This is a full-console app in dart. Its not flutter.
I need to import data from a local json file and send it as response. I need to read the data as array of Map in dart. The data is in following format.
{
"users":[
{
"id":1,
"user":"user1",
"password":"p455w0rd"
},
{
"id":2,
"user":"user2",
"pass":"p455w0rd"
}
]
}
Every where I see the Flutter example which imports flutter/services as rootBundle to read into the JSON file. I do not find a way to implement this in pure dart.
Use dart:io and dart:convert.
Simple example below. Remember to add exception handling if the file does not exist or has wrong format.
import 'dart:convert';
import 'dart:io';
Future<List<Map>> readJsonFile(String filePath) async {
var input = await File(filePath).readAsString();
var map = jsonDecode(input);
return map['users'];
}
Below is the sample code which you can use to synchronously read a text/json file as a string, displays its content and creates corresponding objects. This will work without using any flutter classes.
For reading JSON/txt file,user 'dart:io' package.
Once the file has been read as a string, use JsonDecoder class to convert the json into corresponding data model objects
import 'dart:io';
import 'dart:convert';
const String FILEPATH = "C:\\test\\dartlang\\users.json";
const JsonDecoder decoder = JsonDecoder();
class USER {
int? id;
String? user;
String? password;
//{ } - implies named arguments
USER({this.id, this.user, this.password});
#override
String toString() {
return "{id:$id,user:$user,password:$password}";
}
}
void main() {
List<USER>? eMP;
//synchronously read file contents
var jsonString = File(FILEPATH).readAsStringSync();
//print(jsonString);
//pass the read string to JsonDecoder class to convert into corresponding Objects
final Map<String, dynamic> jsonmap = decoder.convert(jsonString);
//DataModel - key = "users", value = "ARRAY of Objects"
var value = jsonmap["users"];
if (value != null) {
eMP = <USER>[];
//Each item in value is of type::: _InternalLinkedHashMap<String, dynamic>
value.forEach((item) => eMP?.add(new USER(id:item["id"],user:item["user"],password:item["password"] )));
}
eMP?.forEach((element) => print(element));
}
Save the following json file in your filepath.
{
"users":[{"id":1,"user":"user1", "password":"p455w0rd"},
{"id":2,"user":"user2","password":"p455w0rd"}
]
}
First import dart:convert.
You can parse data from a JSON file as follows:
Future<void> readJson() async {
final String response = await rootBundle.loadString('assets/sample.json');
final data = await json.decode(response);
final users = data['users'];
// ...
}

How to send JSON String with POST using ktor kotlin?

How to add JSON String in the POST request using kotlin and ktor?
Printing it out the Json string read from file or even constructed string with Kotlin in the client, the content looks like JSON.
Still, the server cannot recognize the string as JSON, and when I print it in the server, each double quota is back slashed.
The client obviously adds the back slashes, thus the request is not formatted as it should.
Client Kotlin - Ktor code:
import com.google.gson.*
import io.ktor.client.*
import io.ktor.http.*
...
val client = HttpClient(OkHttp) {
install(JsonFeature) {
serializer = GsonSerializer()
}
}
val fileContent = MyClass::class.java.getResource("myfile").readText()
println("fileContent string = $fileContent")
val out = client.post<String> {
url(url)
contentType(ContentType.Application.Json)
body = fileContent
}
the print out looks like this :
{ "name": "myname", "value": "myvalue" }
but the server (I use hookbin by the way to really print out the data without Jackson conversions) prints out:
{ \"name\": \"myname\", \"value\": \"myvalue\" }
The solution is to pass to the HTTP POST request a JSON object not a String object. They look the same when you print them, but of course they are not equally interpreted by the JVM. So, we have just to parse the string. I use the GSON library.
Add the JsonParser -> parseString method and use its object in the client:
import com.google.gson.JsonParser
val fileContent = MyClass::class.java.getResource("myfile").readText()
println("fileContent string = $fileContent")
var bodyAsJsonObject = JsonParser.parseString(fileContent).asJsonObject
println("bodyAsJsonObject = $bodyAsJsonObject")
val out = client.post<String> {
url(url)
contentType(ContentType.Application.Json)
body = bodyAsJsonObject
}

Converting object to an encodable object failed: Instance of 'Offset'

I'm trying to send Dart Offset points by encoding it to Json format using 'dart:convert' library.
I have gone through the documentation https://api.flutter.dev/flutter/dart-convert/jsonEncode.html.
The error I'm getting is for serializing the inbuilt classes.
The following JsonUnsupportedObjectError was thrown while handling a gesture:
Converting object to an encodable object failed: Instance of 'Offset'
How can i serialize inbuilt class like Offset and Paint class, is this the correct way to send the data to server?
TestData class contains Offset point and toJson() function
class TestData {
TestData(this.point);
Offset point;
toJson() {
return{
'point': point,
};
}
}
Encoder function
String jsonEncoder() {
Map testDataMap = this.testDataObj.toJson();
String jsonStringData = jsonEncode(testDataMap);
return jsonStringData;
}
I would return the JSON explicitly:
return { 'point': {dx: "$point.dx", dy: "$point.dy"}, };

"Unexpected Character" on Decoding JSON

The following is the code:
static TodoState fromJson(json) {
JsonCodec codec = new JsonCodec();
List<Todo> data = codec.decode(json["todos"]);
VisibilityFilter filter = codec.decode(json['visibilityFilter']);
return new TodoState(todos: data,
visibilityFilter: filter);
}
Error produced by Android Studio:
[VERBOSE-2:dart_error.cc(16)] Unhandled exception:
FormatException: Unexpected character (at character 3)
Any idea how to make it work?
This is the output of the Json as produced by Redux.
There's a problem with your code as well as the string you're trying to parse. I'd try to figure out where that string is being generated, or if you're doing it yourself post that code as well.
Valid Json uses "" around names, and "" around strings. Your string uses nothing around names and '' around strings.
If you paste this into DartPad, the first will error out while the second will succeed:
import 'dart:convert';
void main() {
JsonCodec codec = new JsonCodec();
try{
var decoded = codec.decode("[{id:1, text:'fdsf', completed: false},{id:2, text:'qwer', completed: true}]");
print("Decoded 1: $decoded");
} catch(e) {
print("Error: $e");
}
try{
var decoded = codec.decode("""[{"id":1, "text":"fdsf", "completed": false},{"id":2, "text":"qwer", "completed": true}]""");
print("Decoded 2: $decoded");
} catch(e) {
print("Error: $e");
}
}
The issue with your code is that you expect the decoder to decode directly to a List. It will not do this; it will decode to a dynamic which happens to be a List<dynamic> whose items happen to be Map<String, dynamic>.
See flutter's Json documentation for information on how to handle json in Dart.
I don't know if that's the case, but I got a similar error when me JSON looks like this
[
{
...
},
]
and not like this
[
{
...
}
]
The comma was causing the issue.
If Anyone came here and your are using dio package to call http request you need to set responseType to plain
BaseOptions options = new BaseOptions(
baseUrl: "<URL>",
responseType: ResponseType.plain
);
I also have similar type of error, Be make sure that the argument of .decode method shouldn't be empty object.
try {
if(json["todos"].isNotEmpty) {
List<Todo> data = codec.decode(json["todos"]);
}
if(json["todos"].isNotEmpty) {
VisibilityFilter filter = codec.decode(json['visibilityFilter']);
}
}
catch(e) {
print(e);
}
Do try this, hope it will work for you.