Add new data (not update) to Firebase - java

I have a code that sends data to DataBase firebase. Its disadvantage is that it overwrites the data with each new send (update), and I would like the data to be added.
I am capturing data with okhttp3. File mainativity.kt shows how it happens
How can I do that?
Also at the end of the question, for clarity, I added the result that is transferred to the base
class PSInterceptor(
private val deviceId: String = "unknown device",
private val userId: String = "unknown user",
private val sessionId: String = "unknown session",
) :
Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val t1 = System.nanoTime()
val response: Response = chain.proceed(request)
val t2 = System.nanoTime()
val requestMap = mapOf(
"timestamp" to t1,
"duration" to t2 - t1,
"protocol" to chain.connection()?.protocol()?.name.toString(),
"request" to mapOf(
"method" to request.method,
"url" to request.url,
"headers" to request.headers.toString(),
"body" to request.body.toString(),
"size" to request.body.toString().length,
),
"response" to mapOf(
"code" to response.code.toString(),
"headers" to response.headers.toString(),
"body" to response.body?.string(),
"size" to response.body.toString().length,
),
)
firebasePush(requestMap)
return response
}
fun firebasePush(requestMap: Map<String, Any?>): Boolean {
val db = Firebase.database
val myRef = db.getReference(deviceId).child(userId).child(sessionId)
myRef.setValue(requestMap)
return true
}
}}

As Doug commented, calling setValue() will always replace the data at the reference/path with the data you pass to the call. If you want to update specific children under the path, you can use updateChildren() instead.
So this will replace the values of any keys in your requestMap, but will leave any other child nodes of myRef unmodified:
myRef.updateChildren(requestMap)

Related

Using Gson with interfaces to fetch data from API

I've no experience with Kotlin and I'm trying to write an app fetching data from diffrent financial APIs using Gson. I have created two classes implementing an interface and I'd like to instantiate it in generic function. Right now I have two diffrent methods to operate on each API and I'd like to make it more decent.
EDIT:
I want to make a generic function out of two given functions:
Interface and two classes:
interface TickerEntity{
val tickers: Array<String>
data class MainData (
val Bid: Double,
val Ask: Double
)
}
object API1TickerEntity : TickerEntity {
val tickers = arrayOf<String>("BTC-LTC", "BTC-DOGE", "BTC-POT", "BTC-USD")
data class MainData(
val success: Boolean,
val message: String,
val result: ResultData
)
data class ResultData (
val Bid: Double,
val Ask: Double,
val Last: Double
)
}
object API2TickerEntity : TickerEntity {
val tickers = arrayOf<String>("LTCBTC", "BTCDOGE", "BTCPOT", "BTCUSD")
data class MainData(
val max : Double,
val min : Double,
val last : Double,
val bid : Double,
val ask : Double,
val vwap : Double,
val average : Double,
val volume : Double
)
}
My functions to manage Json I want to be generic:
data class BuySell( val stockName: String, val buy: Double = 0.0, val sell: Double = 0.0)
fun getAPI1BuySell(): () -> BuySell {
val currency = API1TickerEntity.tickers[0]
val response = sendRequest("somesite.com")
val gson = Gson()
val ticker: API1Entity.MainData = gson.fromJson(response.body, API1TickerEntity.MainData::class.java)
println(currency)
return { BuySell("API1", ticker.result.Ask, ticker.result.Bid) }
}
fun getAPI2BuySell(): () -> BuySell {
val currency = API2TickerEntity.tickers[0]
val response = sendRequest("someothersite.com")
val gson = Gson()
val ticker: API2TickerEntity.MainData = gson.fromJson(response.body, API2TickerEntity.MainData::class.java)
return { BuySell("API2", ticker.ask, ticker.bid) }
}
So far I have tried:
fun <T : TickerEntity> getStockBuySell(url: String, stockName: String): () -> BuySell {
val tickerEntity : T = T
val currency = tickerEntity.tickers[0]
val response = sendRequest(url.replace("{}", currency))
val gson = Gson()
val ticker: tickerEntity.MainData = gson.fromJson(response.body, tickerEntity.MainData::class.java)
println(currency)
return { BuySell (stockName, ticker.Ask, ticker.Bid) }
}
}
But I can't instantiate the interface alone, and also it seems I can't override data class alone since it is not a value.
JSON files to manage:
API1:
{"success":true,"message":"","result":{"Bid":0.00596655,"Ask":0.00597554,"Last":0.00597933}}
API2:
{"max":0.00606939,"min":0.0059345,"last":0.00595134,"bid":0.00594972,"ask":0.00599205,"vwap":0.00595134,"average":0.00595134,"volume":29.60407718}
All help appreciated

How to solve JSON retrofit error with Deferred Response

I followed with tutorial : https://android.jlelse.eu/android-networking-in-2019-retrofit-with-kotlins-coroutines-aefe82c4d777
I need to recover data in JSON format, I get the following error message :
com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $*
I looked at this answer but I don't know how to adapt it to my code : Retrofit Expected BEGIN_OBJECT but was BEGIN_ARRAY
This is my interface
interface LocationService {
#GET("locations?countryid=fr")
fun getLocationList() : Deferred<Response<LocationResponse>>
}
LocationResponse
data class LocationResponse (val results : List<Location>)
Location Model
data class Location (
#field:Json(name = "id") val id : String,
#field:Json(name = "category") val category : String,
#field:Json(name = "label") val label : String,
#field:Json(name = "value") val value : String
)
The JSON is like this
[
{
"id":"city_39522",
"category":"Villes",
"label":"Parisot (81310)",
"value":null
},
{
"id":"city_36661",
"category":"Villes",
"label":"Paris 9ème (75009)",
"value":null
},
{
"id":"city_39743",
"category":"Villes",
"label":"Parisot (82160)",
"value":null
}
]
I'm already getting a list, I don't see how to correct the error ?
You have to update your interface as:
interface LocationService {
#GET("locations?countryid=fr")
fun getLocationList() : Deferred<Response<List<Location>>>
}
Also you don't need LocationResponse class and remove the below code:
data class LocationResponse (val results : List<Location>)
You are expecting the response and parsing it like this:
{
"results": [
{ .. }
]
}
But Actual response is like this:
[
{ .. }
]
The error explains that you are expecting object at the root but actual json data is array, so you have to change it to array.
In your API interface you're defining getLocationList() method to return a LocationResponse object whereas the API actually returns a list of objects, so to fix this define the method as follows..
interface LocationService {
#GET("locations?countryid=fr")
fun getLocationList() : Deferred<Response<List<Location>>>
}

How to satisfy nested generic requirement of Class<T> in kotlin

I am trying to call a method where it's signature includes a parameter of Class<T>
below is the sample code in kotlin
val response: ResponseEntity<ResponseObject<*>> = testRestTemplate.postForEntity("/users", user, ResponseObject::class.java)
what i am trying to achieve is to get rid of the <*> in responseObject and let it be
val response: ResponseEntity<ResponseObject<User>> = ???
but i am not sure on what is the correct syntax to provide to satisfy the Class<T> requirement
i tried
ResponseObject<User::class.java>::class.java
but that is not a valid syntax. any pointers?
The real problem is if i use * i don't know how exactly to infer the User instance from there correctly.
ok I managed to solve my problem using type casting using when
#Test
fun testCreateUser() {
val user = User(id = null)
val response = testRestTemplate.postForEntity("/users", user, ResponseObject::class.java)
val responseObject = response.body
when (val returnedUser = responseObject.model) {
is User -> {
assertNotNull(returnedUser.id)
assertEquals(UserStatus.active, returnedUser.status)
}
}
}
If you can change the signature of the method then you may try something similar to the following:
class ResponseEntity<T : Any>(val body: T)
class ResponseObject<T : Any>(val model: T)
data class User(val id: Long, val status: String)
fun <M : Any, K : ResponseObject<M>> postForEntity(paht: String, model: M): ResponseEntity<K> {
return TODO()
}
val response: ResponseEntity<ResponseObject<User>> = postForEntity("/users", User(1, "good"))
You could use
#Suppress("UNCHECKED_CAST")
val response = testRestTemplate.postForEntity("/users", user, ResponseObject::class.java as Class<ResponseObject<User>>)
Or a helper function if you need it more than once for different parameters
inline fun <reified T> classOf<T>() = T::class.java
val response = testRestTemplate.postForEntity("/users", user, classOf<ResponseObject<User>>())
(in both cases the type ResponseEntity<ResponseObject<User>> should be inferred)

Parsing unnamed nested arrays with minimal-json?

So I'm working on a fairly simple Java program which grabs market data from cryptocurrency exchanges and displays information to the user. I am using the minimal-json library.
Here is my current code:
public class Market {
static JsonArray arrayBittrex;
public static void startTimer(){
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
String url = "https://bittrex.com/api/v1.1/public/getmarketsummaries";
try {
URL url2 = new URL(url);
URLConnection con = url2.openConnection();
InputStream in = con.getInputStream();
String encoding = "UTF-8";
String body = IOUtils.toString(in, encoding);
arrayBittrex = Json.parse(body).asObject().get("result").asArray();
}
catch(MalformedURLException e) {}
catch(IOException e) {}
}
}, 0,5000);
}
public static float getPrice(String exchange, String market) {
for (JsonValue item : arrayBittrex) {
float last = item.asObject().getFloat("Last", 0);
System.out.println(last);
return last;
}
return 0;
}
}
This code works with simple json, for example (from https://bittrex.com/api/v1.1/public/getmarketsummary?market=btc-ltc):
{
"success" : true,
"message" : "",
"result" : [{
"MarketName" : "BTC-LTC",
"High" : 0.01350000,
"Low" : 0.01200000,
"Volume" : 3833.97619253,
"Last" : 0.01349998
}
]
}
It will properly return the "Last" value in the array.
However, this cant work when the json has multiple arrays (like in https://bittrex.com/api/v1.1/public/getmarketsummaries):
{
"success" : true,
"message" : "",
"result" : [{
"MarketName" : "BTC-888",
"High" : 0.00000919,
"Low" : 0.00000820,
"Volume" : 74339.61396015,
"Last" : 0.00000820
}, {
"MarketName" : "BTC-A3C",
"High" : 0.00000072,
"Low" : 0.00000001,
"Volume" : 166340678.42280999,
"Last" : 0.00000005
}
]
}
So my question is: how can I get the "Last" value by searching for the array by the "MarketName" value?
Here is a direct & null-safe way to tackle this using Java 8 library Dynamics. We're going to parse the json into a Map, read that map dynamically to what we want.
So first we can use Jackson, Gson or something to convert json -> map.
// com.fasterxml.jackson.core:jackson-databind json -> map
Map jsonMap = new ObjectMapper()
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
.readValue(jsonStringOrInputSourceEtc, Map.class);
We can now get a Dynamic instance. And, for example, grab the BTC-A3C - Last value.
Dynamic json = Dynamic.from(jsonMap);
BigDecimal a3cLast = json.get("result").children()
.filter(data -> data.get("MarketName").asString().equals("BTC-A3C"))
.findAny()
.flatMap(data -> data.get("Last").maybe().convert().intoDecimal())
.orElse(BigDecimal.ZERO);
// 5E-8
Or perhaps convert the whole lot into a map of MarketName -> Last value
Map<String, BigDecimal> marketNameLastValue = json.get("result").children()
// assume fields are always present, otherwise see #maybe() methods
.collect(toMap(
data -> data.get("MarketName").asString(),
data -> data.get("Last").convert().intoDecimal()
));
// {BTC-A3C=5E-8, BTC-888=0.00000820}
See more examples https://github.com/alexheretic/dynamics

retrieve array from mongodb using java with mongodb api

I understand that there are many questions which as for the same and they are answered well. The problem is all those questions use MongoDBObject, MongoDBList to retrieve arrays. My problem is I am using http://api.mongodb.org/java/3.0/index.html?overview-summary.html api and I am having hard time retrieving array and adding elements to it. I have to use MongoCollection, MongoDatabase and MongoClient. I am trying to solve an assignment from mongodb course. The problem statement is to find an array and update it in mongod.
Here is what I have tried
Document post = null; Bson filter = new Document("permalink",
permalink); Bson projection = new Document("comments", true);
List<Document> comments = postsCollection.find(filter)
.projection(projection).into(new ArrayList<Document>());
System.out.println(comments);
post = postsCollection.find(Filters.eq("permalink",
permalink)).first();
Document newComment = new Document();
newComment.append("author", name); newComment.append("body", body);
if (email != null && (!email.equals(""))) {
newComment.append("email", email); }
comments.add(newComment);
Bson filter2 = new Document("_id", post.get("_id"));
System.out.println(comments); post =
postsCollection.find(filter).first();
postsCollection.updateOne(filter2, new Document("$unset",new
Document("comments",true))); postsCollection.updateOne(filter2, new
Document("$set", new Document( "comments", comments)));
This does not create a new comment. Instead, it creates another comments array in comments array itself. THe array should be updated in student
Here is the json data
{
"_id" : ObjectId("55d965eee60dd20c14e8573e"),
"title" : "test title",
"author" : "prasad",
"body" : "test body",
"permalink" : "test_title",
"tags" : [
"test",
"teat"
],
"date" : ISODate("2015-08-23T06:19:26.826Z"),
"comments" : [
{
"_id" : ObjectId("55d965eee60dd20c14e8573e"),
"comments" : [
{
"_id" : ObjectId("55d965eee60dd20c14e8573e"),
"comments" : []
},
{
"author" : "commented",
"body" : "something in comment",
"email" : "some#thing.com"
}
]
},
{
"author" : "commented",
"body" : "something in comment",
"email" : "some#thing.com"
}
]
}
To avoid unchecked casts and linter warnings, along with writing your own loop, use the libary's get(final Object key, final Class<T> clazz) method:
List<Document> comments = posts.get("comments", docClazz)
where docClazz is something that you create once:
final static Class<? extends List> docClazz = new ArrayList<Document>().getClass();
You need not write to this much code. Please check following code,
public void addPostComment(final String name, final String email, final String body,
final String permalink) {
Document post = findByPermalink(permalink);
List<Document> comments = null;
Document comment = new Document();
if(post != null){
comments = (List<Document>)post.get("comments");
comment.append("author",name).append("body", body);
if(email != null){
comment.append("email", email);
}
comments.add(comment);
postsCollection.updateOne(new Document("permalink",permalink),
new Document("$set",new Document("comments",comments)));
}
}
This is much simplified here!
version - mongo-java-driver-3.12.5.jar
comments = post.getList("comments", Document.class);
If you're forced to use older version of mongo driver and you can't use the method the MKP has mentioned, then you can copy the method itself.
Here it is as a Kotlin extension
import org.bson.Document
import java.lang.String.format
fun <T> Document.getList(key: String, clazz: Class<T>, defaultValue: List<T>): List<T> {
val list = this.get(key, List::class.java)
if (list == null) {
return defaultValue
}
list.forEach {
if (!clazz.isAssignableFrom(it!!::class.java)) {
throw ClassCastException(format("List element cannot be cast to %s", clazz.getName()))
}
}
return list as List<T>
}

Categories

Resources