Write custom document to Cosmos DB with Java API - java

I have a Cosmos DB and want to write different kind of documents to it. The structure of the documents is dynamic and can change.
I tried the following. Let's say I have the following class:
class CosmosDbItem implements Serializable {
private final String _id;
private final String _payload;
public CosmosDbItem(String id, String payload) {
_id = id;
_payload = payload;
}
public String getId() {
return _id;
}
public String getPayload() {
return _payload;
}
}
I can create then the document with some JSON as follows:
CosmosContainer _container = ...
CosmosDbItem dataToWrite = new CosmosDbItem("what-ever-id-18357", "{\"name\":\"Jane Doe\", \"age\":42}")
item = _cosmosContainer.createItem(dataToWrite, partitionKey, cosmosItemRequestOptions);
This results in a document like that:
{
"id": "what-ever-id-18357",
"payload": "{\"name\":\"Jane Doe\", \"age\":42}",
"_rid": "aaaaaaDaaAAAAAAAAAA==",
"_self": "dbs/aaaaAA==/colls/aaaaAaaaDI=/docs/aaaaapaaaaaAAAAAAAAAA==/",
"_etag": "\"6e00c443-0000-0700-0000-5f8499a70000\"",
"_attachments": "attachments/",
"_ts": 1602525607
}
Is there a way in generating the payload as real JSON object in that document? What do I need to change in my CosmosDbItem class? Like this:
{
"id": "what-ever-id-18357",
"payload": {
"name":"Jane Doe",
"age":42
},
"_rid": "aaaaaaDaaAAAAAAAAAA==",
"_self": "dbs/aaaaAA==/colls/aaaaAaaaDI=/docs/aaaaapaaaaaAAAAAAAAAA==/",
"_etag": "\"6e00c443-0000-0700-0000-5f8499a70000\"",
"_attachments": "attachments/",
"_ts": 1602525607
}

Here is my solution that I ended up. Actually it is pretty simple once I got behind it. Instead of using CosmosDbItem I use a simple HashMap<String, Object>.
public void writeData() {
...
Map<String, Object> stringObjectMap = buildDocumentMap("the-id-", "{\"key\":\"vale\"}");
_cosmosContainer.createItem(stringObjectMap, partitionKey, cosmosItemRequestOptions);
...
}
public Map<String, Object> buildDocumentMap(String id, String jsonToUse) {
JSONObject jsonObject = new JSONObject(jsonToUse);
jsonObject.put("id", id);
return jsonObject.toMap();
}
This can produce the following document:
{
"key": "value",
"id": "the-id-",
"_rid": "eaaaaaaaaaaaAAAAAAAAAA==",
"_self": "dbs/eaaaAA==/colls/eaaaaaaaaaM=/docs/eaaaaaaaaaaaaaAAAAAAAA==/",
"_etag": "\"3b0063ea-0000-0700-0000-5f804b3d0000\"",
"_attachments": "attachments/",
"_ts": 1602243389
}
One remark: it is important to set the id key in the HashMap. Otherwise one will get the error
"The input content is invalid because the required properties - 'id; ' - are missing"

Related

Spring Boot - JSON Deserialization

I am getting nested json object and json array as response while making ReST call. Nested json array and json object comes randomly. It may not be present as part of response everytime. I want to deserialize json in such a way that all the fields in json place at the root level of java object.
JSON Response
{
"data": {
"id":"42342sdz",
"details" : {
"username": "Username",
"location": "Location",
"history":[
{
"historyId":"34312cs", "historyDetail":"Some val", "datetime":"Some val",
"myObj":{
"myField" : "Some val"
}
},
{ "historyId":"34312cs", "historyDetail":"Some val", "datetime":"Some val"}
]
}
}
}
Java Object which I want to build by parsing above JSON response.
class ResponseObject {
String id;
String username;
String location;
List<History> historyList;
//Getters and Setters
}
class History {
String historyId;
String historyDetails
String datetime;
String myField;
//Getters and Setters
}
I'm not really sure what you mean when you say that the JSON object comes randomly. If you mean that the fields themselves are random (with random labels), then I'm not confident that you can store them as fields in a Java object like that. However, if you know the fields beforehand, then you can tell Jackson (the JSON deserializer that Spring Boot uses) how to deserialize the object by adding a method into your ResponseObject class that looks like this:
#SuppressWarnings("unchecked")
#JsonProperty("data")
private void unpackNested(Map<String, Object> data) {
this.id = (String) data.get("id");
Map<String, Object> details = (Map<String, Object>) data.get("details");
this.username = (String) details.get("username");
this.location = (String) details.get("location");
List<Map<String, Object>> originalHistory = (List<Map<String, Object>>) details.get("history");
historyList = new ArrayList<>();
if (originalHistory != null) {
for (Map<String, Object> h : originalHistory) {
History history = new History();
history.setHistoryId((String) h.get("historyId"));
history.setHistoryDetails((String) h.get("historyDetail"));
history.setDatetime((String) h.get("datetime"));
Map<String, Object> myObj = (Map<String, Object>) h.get("myObj");
if (myObj != null) {
history.setMyField((String) myObj.get("myField"));
}
historyList.add(history);
}
}
}
If you don't know the names of the fields, then I think the best you can do is store it into a flat Map<String, Object>.

How to map dynamic fields from MongoDB to model Spring Boot

I try to map dynamic fields of a document in mongodb collection to a model in spring boot project,
well this is the document from mongodb
Example of MongoDB Document
{
"_id":"5f78e5599fe104f27eb61333",
"conducteur":{
"cin":"F21458",
"nomComplet":"Ahmed Ayoub",
"idConducteur":"4"
},
"vehicule":{
"matricule":"4585|45| a",
"engin":"Benne",
"idVehicule":"1"
},
"date":"21-11-2019 22:07:44",
"site":"Paris",
"rating":37.04,
"etat":true,
"imageUrl":null,
"motif":"nothing",
"controlleur":{"cin":"F44585","nomComplet":"Test","idController":"2"},
"checklistConducteur":[false,true,false,true,true],
"checklistEquipement":[false,false,false,true,true],
"checklistEngin":[false,true,true,false,true,false,true],
"checklistAttelage":[true,true,true,false,true]
}
Then I create the model with the standards or fixed fields and one field to handle dynamic properties, the model seems like
#Document(collection = "checkList")
public class CheckList {
private String id;
private Map<String, String> conducteur;
private Map<String, String> vehicule;
private Date date;
private String site;
private double rating;
private boolean etat;
private String imageUrl;
private String motif;
private Map<String, String> controlleur;
private Map<String, Boolean[]> catchAll = new HashMap<>();
public Map<String, Boolean[]> getCatchAll() {
return catchAll;
}
#JsonAnySetter
public void setCatchAll(Map<String, Boolean[]> catchAll) {
this.catchAll = catchAll;
}
}
So what i want is to combine or map checklistConducteur, checklistEquipement, checklistEngin and checklistAttelage from mongodb document to the field catchAll, or to get nested inside it.
I tried to use the annotations like #JsonAnyGetter, #JsonAnySetter, or to create a class CatchAll have these properties, but give me the same result:
API Response:
{
"id": "5f78e5599fe104f27eb61333",
"conducteur": {
"cin": "F21458",
"nomComplet": "Ahmed Ayoub",
"idConducteur": "4"
},
"vehicule": {
"matricule": "4585|45| a",
"engin": "Benne",
"idVehicule": "1"
},
"date": "21-11-2019 22:07:44",
"site": "Paris",
"rating": 37.04,
"etat": true,
"imageUrl": null,
"motif": "nothing",
"controlleur": {
"cin": "F44585",
"nomComplet": "Test",
"idController": "2"
},
"catchAll": {}
}
what i want as result from API:
"catchAll": {
"checklistConducteur":[false,true,false,true,true],
"checklistEquipement":[false,false,false,true,true],
"checklistEngin":[false,true,true,false,true,false,true],
"checklistAttelage":[true,true,true,false,true]
}
If anyone has a solution or suggestion please help me, I will be thankful.

Using jackson to extract inner JSON array list

My API needs to return a list of entry objects from the JSON below. I am using jersey and jackson. I would ideally like to only create a java class PermissionEnty , and my API to return a list of PermissionEntry objects from the JSON. I am not able to deserialize using the below approach? Can someone advise what could be the issue? I have added UNWRAP_ROOT_VALUE so I presume the 'list' node it ignored, and I would get items below 'list' node.
public class PermissionEntry {
private String id;
private String displayName;
private String memberType;
}
and the json;
{
"list": {
"pagination": {
"count": 5,
"hasMoreItems": false,
},
"entries": [
{
"entry": {
"displayName": "norma",
"id": "norma",
"memberType": "PERSON"
}
},
{
"entry": {
"displayName": "clara",
"id": "clara",
"memberType": "PERSON"
}
},
{
"entry": {
"displayName": "michael",
"id": "mich",
"memberType": "PERSON"
}
}
]
}
}
PermissionEntries
public class PermissionEntries {
#JsonProperty(value = "entries")
#JsonDeserialize(using = PermissionEntryDeserializer.class)
private List<PermissionEntry> entries;
public List<PermissionEntry> getEntries() {
return entries;
}
public void setEntries(List<PermissionEntry> entries) {
this.entries = entries;
}
}
Below is the deserializer that I am using
public class PermissionEntryDeserializer extends JsonDeserializer<List<PermissionEntry>> {
private static final String ENTRY = "entries";
private static final ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
private static final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, PermissionEntry.class);
#Override
public List<PermissionEntry> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
ObjectNode objectNode = mapper.readTree(jsonParser);
JsonNode nodeEntries = objectNode.get(ENTRY);
if (null == nodeEntries // if no ENTRY node could be found
|| !nodeEntries.isArray() // or ENTRY node is not an array
|| !nodeEntries.elements().hasNext()) // or ENTRY node doesn't contain any entry
return null;
return mapper.readerFor(collectionType).readValue(nodeEntries);
}
}
Service API
public Optional<List<PermissionEntry>> getPermissionsForGroup(String groupName) {
Response response = getTarget()
.path("/api/group/" + groupName + "/members")
.request()
.get();
PermissionEntries list = response.readEntity(PermissionEntries.class);
}
I don't understand what you mean in this question 'Can someone please tell me how many java classes do I need to create to get a list of entries.' but you had already an entry object called PermissionEntry. You will have the list of this object.
This is the jersey client of your data with jakcson .
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
String URL = "http://{{host}}:{{port}}/entry";
WebResource webResourceGet = client.resource(URL);
ClientResponse response = webResourceGet.accept("application/json").get(ClientResponse.class);
String output = response.getEntity(String.class);
ResponseList responseList= mapper.readValue(output , ResponseList .class);//here is the permissionEntry list that you wil have
Also, you should create an object given name as Pagination for pagination that is in the json data. You can make an another object that includes List<PermissionEntry> and Pagination called ResponseList.

Parse JSON response with numbers as Object

I have this JSON response, from a remote server and i really hope i can get help.
{
"data": {
"6111": {
"prereq": "0",
"mast": "The Master Tree"
},
"6112": {
"prereq": "1",
"mast": "Another Master Tree"
}
}
}
I use GSON to parse JSON, using the #SerializedName and #Exposeto obtain the value into a custom Model. But i do not understand how to get past the
"6111"
"6112"
I have checked other questions via the gson tag, to no avail.
try this
Iterator<String> iter = json.keys();
while (iter.hasNext()) {
String key = iter.next();
try {
Object value = json.get(key);
} catch (JSONException e) {
// Something went wrong!
}
}
///////////////////update////////////////////
JSONObject issueObj = new JSONObject(jsonContent);
Iterator iterator = issueObj.keys();
while(iterator.hasNext()){
String key = (String)iterator.next();
JSONObject issue = issueObj.getJSONObject(key);
// get id from issue
String _pubKey = issue.optString("id");
}
If you're using Gson, any time you have an object with keys you don't know ahead of time, you can just use Map instead of a custom object.
In this case, each element of the Map will be some "known" data structure, so you would use Map<String, MyObject>.
Your top-level class:
public class MyResponse {
#SerializedName("data")
#Expose
private Map<String, MyObject> data;
...
}
And your map's value class:
public class MyObject {
#SerializedName("prereq")
#Expose
private String prereq;
#SerializedName("mast")
#Expose
private String mast;
...
}
In the specific case of the json text you posted, you would then be able to use these objects like this:
response.getData().get("6111").getMast();
But you can also do anything you could normally do with a Map:
Map<String, MyObject> data = response.getData();
for (String key: data.keySet() {
...
}
for (MyObject obj : data.values()) {
...
}

Firebase Android: how do I access params nested in data via RemoteMessage?

via this shape:
{
"to": "000",
"priority": "high",
"data": {
"title": "A Title",
"message": "A Message",
"link": {
"url": "http://www.espn.com",
"text": "ESPN",
}
}
}
how can I access "url" and "text"?
String messageLink = remoteMessage.getData().get("link");
gets me:
{"text":"ESPN","url":"http://www.espn.com"}
but how do I drill deeper?
remoteMessage.getData().get("link").get("text");
doesnt quite work... I have also attempted JSONObject:
JSONObject json = new JSONObject(remoteMessage.getData());
JSONObject link = json.getJSONObject("link");
but this gives me try catch errors...
Any help and direction as always is greatly appreciated!
I would use gson and define a model class. The remote message gives you a Map<String, String> and their is no matching constructor for creating a json object.
Add gson to your build.xml:
compile 'com.google.code.gson:gson:2.5'
Create a notification model:
import com.google.gson.annotations.SerializedName;
public class Notification {
#SerializedName("title")
String title;
#SerializedName("message")
String message;
#SerializedName("link")
private Link link;
public String getTitle() {
return title;
}
public String getMessage() {
return message;
}
public Link getLink() {
return link;
}
public class Link {
#SerializedName("url")
String url;
#SerializedName("text")
String text;
public String getUrl() {
return url;
}
public String getText() {
return text;
}
}
}
Deserialize a notification object from the remote message.
If all your custom keys are at the top level:
Notification notification = gson.fromJson(gson.toJson(remoteMessage.getData()), Notification.class);
If your custom json data is nested in a single key for example "data" then use:
Notification notification = gson.fromJson(remoteMessage.getData().get("data"), Notification.class);
Note in this simple case the #SerializedName() annotations are unnecessary since the field names exactly match the keys in the json, but if you for example have a key name start_time but you want to name the java field startTime you would need the annotation.
As simple as that:
String linkData = remoteMessage.getData().get("link");
JSONObject linkObject = new JSONObject(linkData);
String url = linkObject.getString("url");
String text = linkObject.getString("text");
Of course, together with proper error handling.
Faced this issue when migrating from GCM to FCM.
The following is working for my use case, so perhaps it will work for you.
JsonObject jsonObject = new JsonObject(); // com.google.gson.JsonObject
JsonParser jsonParser = new JsonParser(); // com.google.gson.JsonParser
Map<String, String> map = remoteMessage.getData();
String val;
for (String key : map.keySet()) {
val = map.get(key);
try {
jsonObject.add(key, jsonParser.parse(val));
} catch (Exception e) {
jsonObject.addProperty(key, val);
}
}
// Now you can traverse jsonObject, or use to populate a custom object:
// MyObj o = new Gson().fromJson(jsonObject, MyObj.class)

Categories