Mapping Uknown/New MongoDB fields to Map/List - Java - java

I have a Mongo database and am building a REST API using the Play Framework in Java. I would like to map my Mongo query results to POJOs using some kind of mapper like MongoJack or PlayJongo or something similar.
My problem is that I don't want to have to specify all/new fields in my POJO. What I would like to do is to put all un-mapped fields into a catch all Map as part of the POJO. That way new fields that are added can still be returned in the API response without the Java application having to be aware of them.
Here's an example:
Mongo Document:
{
_id: 1234,
mappedString: "Foo",
mappedInt: 544,
unMappedString: "Test",
unMappedInt: 12
}
Java Class
public class Bar {
private int _id;
private String mappedString;
private int mappedInt;
private Map<String, Object> unMappedFields;
}
I would then expect the unMappedFields field to have two entries in it being "unMappedString" and "unMappedInt" with values "Test" and 12 respectively.
Is there a way to achieve this with one of the Mongo POJO mappers? I've not been able to find any information on if this is possible or not.

Why not to store in mongo as:
{
_id: 1234,
mappedString: "Foo",
mappedInt: 544,
unMappedFields:
{
unMappedString: "Test",
unMappedInt: 12
}
}
I have worked with morphia and this case would be directly parsed.

Related

How to pass a list of integers as json data to a Spring Boot rest API without a custom intermediate object?

How to pass a list of integers as json data to a Spring Boot rest API without creating a custom intermediate object? I know you can create a DTO sort of object to map the request and I've done that successfully but is there a way to receive it as a collection/array of IDs without having to create a class just for this purpose? I tried to do it without any luck. Also tried to map it to a collection of the Entity the IDs refer to also without any luck. A way to do this, or alternatives that are more idiomatic to Spring Boot would be welcomed.
Sample of request body that's being sent to the endpoint:
{
"products": [
"1",
"2"
]
}
It seems answer of fps should be suitable, but here one caveat
Even if your class is annotated as #RestController, you need to use #RequestBody when using Map as input (with POJO this annotation can be avoided)
Bellow code works fine:
#PostMapping("/products")
#ResponseStatus(value = HttpStatus.OK)
public Object createProducts(#RequestBody Map<String, List<String>> input) {
return input;
}
More agile way is using Jackson classes.
It allows to use not only Strings, but all "native" json structures.
You can check JsonNode.isNumber(), JsonNode.isTextual(), JsonNode.isArray(), JsonNode.isObject() and so on.
#PostMapping("/products")
#ResponseStatus(value = HttpStatus.OK)
public Object createProducts(#RequestBody Map<String, JsonNode> input) {
return input;
}

Read CF environment nested JSON with #ConfigurationProperties

I have an application on SpringBoot that is deployes to Cloud Foundry. It has a bound service that attaches some information to VCAP_SERVICES that I can use in my application.properties file to receive security related data.
This services variables are in the user-provided sub-json of whole VCAP_SERVICE environment variable and looks like this:
"user-provided": [
{
"credentials": {
"auth-scopes": {
"group-1": "superadmin",
"group-2": "admin"
},
"base64ClientCredential": "SSBBTSBOT1QgVEhBVCBTSUxMWSA6LUQ=",
"client-id": "my-app",
"client-secret": "g1234567890",
"token-info-uri": "https://XYZ.predix.io/check_token",
"user-info-uri": "https://XYZ.predix.io/userinfo"
},
"label": "user-provided",
"name": "my-app-creds",
"syslog_drain_url": "",
"tags": [],
"volume_mounts": []
}
]
In my application.properties I have
security.properties.client-id=${vcap.services.my-app-creds.credentials.client-id}
security.properties.client-secret=${vcap.services.my-app-creds.credentials.client-secret}
security.properties.token-info-uri=${vcap.services.my-app-creds.credentials.token-info-uri}
security.properties.user-info-uri=${vcap.services.my-app-creds.credentials.user-info-uri}
security.properties.auth-scopes=${vcap.services.my-app-creds.credentials.auth-scopes}
I also created class SecurityProperties that reads those properties and stores in JAVA:
#Component
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "security.properties")
public class SecurityProperties {
String clientId;
String clientSecret;
String tokenInfoUri;
String userInfoUri;
//Required getters and setters
}
And this class successfully binds those fields to information received from application.properties.
The problem occurs when I want to receive Map:
"auth-scopes": {
"group-1": "superadmin",
"group-2": "admin"
}
I tried adding to SecurityProperties class field:
HashMap<String, String> authScopes;
But it fails
I also tried with inner class
AuthScopes authScopes;
public static class AuthScopes {
//various different implementations
//to read this but no success at all
}
I ran out of other ideas. I just can't figure out how to get this. I will also accept that auth-scopes json from VCAPS will be read as a string, and then I will parse it - no problem. The issue is that even if I add to SecurityProperties field String authScopes it is bound with exactly this string: "${vcap.services.my-app-creds.credentials.auth-scopes}". Nothing useful.
If you have any idea - please share.
Spring Boot provides the vcap. properties via the CloudFoundryVcapEnvironmentPostProcessor. That class reads the VCAP_SERVICES JSON and flattens it into a set of discrete properties. If you add Spring Boot actuators to your application and access the /env endpoint, you will see the set of vcap. properties that get created.
In your example, the properties will contain these values:
"vcap": {
...
"vcap.services.my-app-creds.credentials.auth-scopes.group-1": "******",
"vcap.services.my-app-creds.credentials.auth-scopes.group-2": "******",
}
There is no vcap.services.my-service.credentials.auth-scopes property in that list, because the JSON get completely flattened. The property names use the dot notation to reflect the JSON structure, but the hierarchy is not preserved in the set of properties. In short, what you are trying to do won't work with Boot properties.
One alternative would be to set those values as a string instead of a hash in the JSON, as in "auth-scopes": "group-1:superadmin,group-2:admin" or "auth-scopes": "group-1=superadmin,group-2=admin". Your SecurityProperties could then get the value of vcap.services.my-service.credentials.auth-scopes and parse the String into a Map.

Using Swagger-annotation to represent a complex property as a String

In a Java/Spring ReST application, I'm using swagger-annotations 1.3.7 I have a number of small classes (for example, GenderCode) that I use as properties in my ReST models. These classes have a single public property, called value. Using Jackson, my APIs can accept a simple String s and construct an instance of, say, GenderCode with its value set to s. Similarly, it can serialize a GenderCode as a simple String (which of course represents the value of value).
I would like my Swagger documentation to represent these objects as simple strings, since that represents what the JSON will look like. Instead it represents an complex type with a "value" key:
{
"genderCode": {
"value": ""
},
...
}
It should look simply like this:
{
"genderCode": "",
...
}
Here's what the Java model would look like:
public class Person {
#JsonProperty("genderCode")
#Valid
#KnownEnumValue
#ApiModelProperty(value = "GenderCode", dataType="string", required = false,
allowableValues=GenderCode.POSSIBLE_VALUES_DISPLAY)
private GenderCode genderCode;
...
}
Here's the definition of that property within the API definition file that Swagger generates:
"genderCode":{"enum":["ANY","M","F"],"description":"GenderCode","required":false,"type":"GenderCode"}
I've tried using an OverrideConverter, but that had no effect. Any thoughts on how this can be done?

How should I store this set of data into a Mongo Collection

I have the following Data Structure
{
"routers": [
{
"name": "",
"ip address": "",
}
],
"devices": [
{
"name": "",
"mac address": [
<address>,
<address>,
<address>
],
}
],
"connection": [
{
"source":"<name>",
"destination":"<name>",
}
]
}
I want to store this in a Mongo Database (in a Java web application). I just need some opinions on how to structure this.
Do I put all of this JSON into one Mongo Collection, named "Environment"?
I want to map the data to Java Objects, do I have three separate Objects "Router","Device" and "Connection" OR do I make one Java object "Environment" and have the three Java objects listed above in that particular object? I'm feeling like this second option is the best.
I'm pretty much looking for some advice on the best way to design this, instead of trying to change it later on.
Not that it matters, but I am going to also be using Jongo
EDIT to show potential class:
private class Environment {
private List<Router>;
private List<Device>;
private List<Connection>
...
}
There would be multiple "Environment", essentially what we are building is a configuration management system so there are different "Configurations" of Environments.
I would do lots of documents in 1 collection. I find it easier to search that way and easier to write pojo-like code against.
Your data modelling question has been answered before: Mongodb: multiple collections or one big collection w/ index
I would use any of the very many JSON utility libs (e.g. GSON) for Java.
Create a pojo class that has all of the fields you require in the data structure:
private List<Router> routers;
...
private static class Router {
private String name;
...
}
Create getters/setters if you need to
Use your utility to convert to/from, example of a utility class below
import com.google.gson.Gson;
import com.google.gson.JsonParser;
private static Gson gson = new Gson();
private static final JsonParser parser = new JsonParser();
public static <T> T toPojoObject(String json, Class<T> clazz, Gson aGson) {
return aGson.fromJson(createJSONObject(json).getAsJsonObject(), clazz);
}
public static JsonElement createJSONObject(String json) {
return parser.parse(json);
}
(OK not the most wonderful example of using GSON, but just illustrating the point)
Making one java object "environment" and including others in it will be better. e.g I am using JPA based object datastore mapper and in that you can use "#embedded" to embed other objects in that entity if you have all the objects embedded in one entity you can persist all the objects using a single call to database otherwise you have to hit the database thrice for persisting the three objects.

Create temporary Java object for JSON

What I want to do is create a temporary object on the fly so I can translate it to JSON and send it back to the JSP page for use by the JavaScript. FYI I'm using Java Jackson library to translate a Java Object into JSON.
The JavaBean Class for this object looks something like this.
public class MonthlyAnalysisBean {
private Date monthlyProcessDate;
private Double activeInventory;
private Double inactiveInventory;
private Double excessInventory;
/* Set... Get.... Bla bla methods */
}
I need an object that looks like this in JSON.
{
"date": "2014-04-04",
"Active": 100.00,
"Inactive": 10.23,
"Excess": 2.99
}
Basically I just need to nicely rename and format the same fields. Is there any way to do this In Java without creating another JavaBean and creating the new object?
It would be so easy in JavaScript to just create a new object, send the JSON and be off on our merry way.
Well if you don't want to "create another Javabean" Map<String, String> is an option.
You could just do
Map<String, String> map = new HashMap<String, String>();
map.put("date", monthlyAnalysisBean.getMonthlyProcessDate());
...
Or try to actually use Jackson using #JsonProperty
#JsonProperty("date")
public Date getMonthlyProcessDate() {
return monthlyProcessDate;
}
I've done sth like that with annotations from javax.xml.bind.annotation . I used wink library to marshal/unmarshal JAXB-annotated objects into JSON, but it's mostly for REST web services. I guess your Jackson should suite fine.
#XmlAttribute(name = "date")
private Date monthlyProcessDate;

Categories

Resources