I have the problem related to parsing Firestore Timestamp in Android app.
Summary
In my Firebase Firestore, I have a collection of comments. Every comment looks like this
(time is of type Timestamp)
I've built REST API using Cloud Functions. There is a GET method for comments endpoint. Sharing the whole code doesn't have sense, the important fact is, the response from that endpoint looks like this
As you can see, the response looks good, everything's okay.
The problem
Now, I have the Android app using Retrofit to communicate with the API. It makes a request to aforementioned endpoint and gets the response (response is a <List<CommentResponse>>).
import com.google.firebase.Timestamp
data class CommentResponse(
val placeId: String,
val userUid: String,
val userDisplayName: String,
val userPhotoUrl: String,
val time: Timestamp,
val text: String
)
But when I do Log.d(response.body), I get (I cut the unimportant data)
[CommentResponse(placeId=opactwo, userUid=e09E...82, userDisplayName=Bartek Pacia, userPhotoUrl=https: .../photo.jpg, time=Timestamp(seconds=0, nanoseconds=0), text=This place is very beautiful :D)]
Timestamp vanished. It's not null, but it points to the beggining of the Epoch (1.1.1970). It's just 0 sec, 0 nanosec. I don't use any custom converters or whatever, just the "beginner level Retrofit".
And, well, that's it.
I've no idea why the Timestamp changes to 0,0.
I'd be very grateful if somebody could help me.
Thank you in advance.
You're assuming that Retrofit knows how to deserialize the timestamp based on its default JSON serialization from the server. It doesn't know how to do that. You're going to have to manage that yourself.
When your function serializes the timestamp, it's using the default serialization of the contents of the document. It's looking at the Timestamp object and saying "hey, there's an object, so in order to serialize that, I'm just going to copy all of its properties into the output". If you look at the source for Timestamp, you'll see that it's using properties _seconds and _nanoseconds to hold the components of the Timestamp. So, that explains the JSON output you see.
On the Java (or Kotlin) client side, all that is effectively going to be just a Map object, and the type information is gone (and it wouldn't be helpful as JavaScript stuff doesn't simply map to Java stuff). No one knows that it was a Timestamp type object that came across the wire. All it knows is that there's an object with _seconds and _nanoseconds.
What you need to do is put some smarts into your code (maybe as a hint to Retrofit in the form of a custom converter) to help it recognize what's in that time JSON object, and convert that to a Timestamp object locally using the java Timestamp constructor.
Or, if you don't mind losing a few nanoseconds of precision, just have the function convert the Timestamp into a the time in milliseconds, send that over the wire, and have the client simply convert that to a java Date object.
You might also want to stop depending on the internal details of the JavaScript Timestamp object. Since _seconds and _nanoseconds are effectively private details, you are depending on something that might change in the future. Consider instead explicitly serializing the Timestamp in your function using the data from its public methods getSeconds() and getNanoseconds().
Related
While writing code for RecyclerView to get data I figured out there's a data class in Kotlin.
Following codes are taken from two different projects which are linked above.
#Serializable
data class MarsPhoto(
val id: String,
#SerialName(value = "img_src")
val imgSrc: String
)
class Contacts {
#SerializedName("country")
private val country:String? = null
fun getCountry():String?{
return country
}
}
I know that both classes are doing same job. So what does differentiate them? I also wonder in the MarsPhoto data class how they can get the id without declaring SerialName just the way they did for imgSrc. (I am just on the way to learning kotlin now, so I'm absolute beginner).
Basically for "data" class the compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair
toString() of the form "MarsPhoto(id=1, imgSrc=asdf)"
componentN() functions corresponding to the properties in their order of declaration.
copy()
You can read a lot more at enter link description here
On the SerializedName part of your question. if you are dealing with Gson lib by default it is using fields name as "SerializedName". And only if you want to use something different then field name, you can use SerializedName annotation and pass your custom value there. But usually, everybody just writes #SerializedName() with duplication of field names as value for every field.
It's a good idea if you are receiving and Serializing data from server from Json. Because Backend developers can use a bad keys in response, which you don't want to use in your code, so #SerializedName will be the only place where you will have to see this key, and you can name your fields however you like.
#Serializable used to mark class as serializable to disk or like into a file( alternative is Parcel able in android) special useful in case of process death or configuration changes and #SerializedName("country") used for json parsing when u receive the response from server
You get the id without #SerializedName because the JSON property field is the same as your variable name, but imgSrc and img_src is not. Still, even if they are the same, you should always use #SerializedName, because your variable names could be converted to random letters during code optimization, and obfuscation.
This might be a very silly question, but I am logging the methods that are triggered in my app as strings. When an issue is submitted, I would like to automatically input the text of the strings as parameters for methods. E.g:
For method:
fun assignPot(potType: PotType, ball: DomainBall, action: PotAction) {...}
I'd like to somehow call method:
assignPot(FOUL(2, BLUE(5), SWITCH))
From String:
"FOUL(2, BLUE(5), SWITCH)"
The only workaround I can think of is to split the string and create a when -> then function to get actual classes from strings, but I wondered if there's a more concise way for this.
This is not what you want to do. You should design your app in a way that prevents users from providing input similar to actual code.
However, you can achieve this. Complex parsings like this oftenly use regex-based approaches.
As you said, you should map your string part to class. If your PotType is enum, you can do something like
val re = Regex("[^A-Za-z\s]")
val reNumbers = Regex("[^0-9\s]")
// get classes
val classNames = originalString.replace(re, "").split(" ")
// get their constructor numerical arguments
val classArgs = originalString.replace(reNumbers, "").split(" ")
After that you can implement mapping with when expression. You probably will use some collection of Any type.
As you see, this sadly leads you to parsing code by code. Concise way to solve is to implement your own script compiler/interpreter and use it in your application :) That will later lead you to dealing with security breaches and so on.
If you are logging problematic method calls and want to repeat them immediately after issue is submitted, you probably want to programatically save the calls to lambdas and call them when you receive an issue log.
I am trying to fetch an entire node in one shot from Firebase Real-time database as type Any. The code that I use is as follows:
val offerDetails = p0.child(querykey).child("Offers").child(offerkey).getValue(Any::class.java)
Log.d("MyMessage",
offerDetails.toString())
The data is obtained perfectly as the Log returns:
{offer=90, ****ID=********, deliveryHour=0, mobile=******, type=2, deliveryMinute=15, offerComment=******}
However, I am unable to fetch the individual data like the offer, type, etc hereon. Can someone help me out?
P.S. I can get each of the datapoints individually from Firebase. But I am trying to avoid that and instead get the entire node in one shot.
When you are using the following line of code:
val offerDetails = p0.child(querykey).child("Offers").child(offerkey).getValue(Any::class.java)
The type of the object is Any and not Offer, so you can access its properties. When you are using:
Log.d("MyMessage", offerDetails.toString())
You are just printing in the logcat the String representation of your offerDetails object. If you need to access its properties, then you should cast that object to an object of type Offer.
Log.d("MyMessage", (offerDetails as Offer).offer)
In this way, you are telling the compiler that the object is of type Offer and not Any. In this case, the output in the logcat will be:
90
Edit:
You can also access the properties using the following line of code:
val offer = p0.child(querykey).child("Offers").child(offerkey).child("offer").getValue(String::class.java)
Log.d("MyMessage", offer)
Same output (90) in the logcat.
Firebase Supported Data Types
Your object is most likely returning as a Map<String, Object> object. Maybe use the map as you can read/write it better than an Any object
I'm a bit confused, as from a long time i am saving the json response directly to an ArrayList> and displaying to my listView, but now, looking on other people code i noticed that they are using POJO class to interact with JSON, Is it is better way? if it is please explain why? cause using POJO means I have to write extra code, But if saving the response directly to the arraylist make my work done, then why should i use a POJO class?
So, Pojo usage better due to OOP pattern, because you work at runtime with your Java object without intermediate Json parse. Manual json parsing too ugly due to code style(its my opinion).
But if saving the response directly to the arraylist make my work done
If, you collect your object in Maps, you can apply different features out of the box(sort, compare etc) to your map, but in case when your map contains POJO instead of JSONS.
Encapsulation. When you work with dates for examples or with type, its pretty good to use getters/setters for data mapping instead of manual parsing again and again.
4.Object scaling and mapping:
Lets image that we have some object user:
public class User{
int id;
#SerializedName("specific_id_for_blah_blah")
private int mSpecId
#SerializedName("date_of_birthaday")
private String mBDay;
public Date getBirthday() {
return new Date(mBDay);
}
}
What I want to say by this example.
You can map your json to POJO with one line of code only
User user = new Gson.fromJson(response, User.class);
Pretty simple isn't?.
Name serialization. When your response contain key name which looks to long or weird, you can use your own style naming with easy changes, just with small annotation. mSpecId returns value of "specific_id_for_blah_blah"
Platform specific encapsulation. You can use only platform specific object at your runtime, instead parsing operations in your business logic. String with data -> Date or Calendar
Also you can override Object methods in your POJO (equals, hashcode, toString) for your logic spec. operations.
If your serverside change some key you can change name of key in POJO instead looking through where you parse it before. IN same case you can add new field and setter/getter, if some of parameter will be added to your response
There is no right and wrong answer here. It all depends on your use case. If your solution works, and you are happy with it, I don't see why do you need to change it.
If I had to choose, I would go with a POJO class to represent the response, but this is a subjective opinion. I think that you have the following benefits:
It's cleaner - having a separate, dedicated class to represent your payload gives you the ability to be more specific in your code. You are no longer manipulating Maps of key - value pairs, but instances of a specific class, that can have a more specific behaviour. You can specify natural ordering, criteria for equality, etc - things that may be useful for your program's logic
It's simpler - I would prefer calling a getter every time then accessing a map by a property name and getting an Object back. The logic of the program will be much simpler and safer.
It's better in terms of OOP best practices - the whole point behind OOP is to have objects, that define properties and behaviours. IMHO, using POJOs to represent responses forces you to adhere more closely to best practices.
There are also some cases that will fit the no - POJO approach better - for example, if you only display your data, not manipulating it in any way inside the app. Or if you want to shave off some time for the complex parsing that may be needed if you are trying to inflate object hierarchies.
My best suggestion is - profile your app, check your use cases and make an educated decision which approach is better.
I have a Json of this type :
{
"4f958ef28ecd651095af6ab6": {
enormous JsonObject
}
}
the "4f958ef28ecd651095af6ab6" is different each time (but I know what it will be as it is a parameter of my api call), it corresponds to the id of the following object. I have a Gson-configured model to parse the enormous JsonObject.
My question is : is it performant to use simply
new JSONObject(jsonresponse).getJSONObject("4f958ef28ecd651095af6ab6")
and parse with Gson from there ?
Is there a better way to do so ?
I guess the real question would be, what does "new JSONObject(String)" realy do ?
Thanks
What you are doing is:
You load all the Json string into the phone memory (memory issue + long time to load entirely)
You create a big JSONObject (same issues) in order to have access to each key.
You write few code but this is not the most performant solution.
To minimized the memory impact and accelerate the operation of objects' creation, you can use Gson in stream mode.
By directly read the input stream, you avoid to load too much data and you can directly start to populate your object piece by piece.
And about the JSONObject, it will mostly check if your json string is correct (or it will throw a JsonException) and it will let you look into the object when you search for a key and its value.
I would recommend use hybrid (native and gson) since i am not sure how to get unknown jsonobject with GSON.
You need to get your response as a JSONArray, then itarete for each JSONObject. You can experiment parsing code as trying. Please check JSONArray.getJSONObject(int index) method. Then we can use GSON to get our data model to get known attributes.
If you can post complete json data, we can give it chance to parse together.