MongoItemReader: how to deal with custom fields? - java

MongoItemReader provided by spring batch has method setFields:
public void setFields(java.lang.String fields)
JSON defining the fields to be returned from the matching documents by
MongoDB.
Parameters:
fields - JSON string that identifies the fields to sort by.
I have a class:
public class Raw {
private String id;
private String version;
private String client;
private String appName;
private String os;
// getters & setters
}
And I have data in mongodb like that:
{
"_id" : ObjectId("58a3373e1e041a1191cd5d6d"),
"Version" : "123",
"Client" : "SomeClient",
"MobilePlatform" : "iphoneos",
"AppName" : "MyAppName",
"Os" : "Windows 10"
}
- so as you can see all fields names start with capital letter.
Now I need to read data from mongo with spring batch.
And I need to map somehow fields in my Raw class to data in mongo DB so I will be able to fetch data.
I suspect that setFields method is just for such cases.
But I am relatively new to mongo and spring batch also,
so I would like to ask how to do that?
Which JSON should I put into setFields method?
Or probably there are some other options?
Any help is greatly appreciated.

I found the anwer by myself in documentation: http://docs.spring.io/spring-data/data-mongo/docs/1.4.2.RELEASE/reference/html/mapping-chapter.html
#Field annotation can be used and it works.

Related

How to Parse JSON object partially to Java object

I am working on a big json output file which is coming as response but I want to parse only some of the fields in my logic.
For ex: The JSON looks like this
{
"lastName":"Smith",
"address":{
"streetAddress":"21 2nd Street",
"city":"New York",
"state":"NY",
"postalCode":10021
},
"age":25,
"phoneNumbers":[
{
"type":"home", "number":"212 555-1234"
},
{
"type":"fax", "number":"212 555-1234"
}
],
"firstName":"John"
}
I have created the necessary JAVA classes and mapping the JSON Object to Java object using GSON. Since, the above JSON is just a sample one in my case I have big one which is generating around 15 classes.
Currently i have to create following classes files:
- Employee.class
- Address.class
- PhoneNumber.class
I want to avoid creating PhoneNumber.class and its nested class using GSON.
Basically My query is like in above json I don't want phoneNumbers and its internal objects so how can i ignore those fields so that i have to construct less Java Class files and still it is mapped to Java Object.
So I want to avoid making classes for PhoneNumbers fields and the nested fields inside PhoneNumbers.
As suggested, Employee and Address classes are all you need:
public class Employee {
private String firstName, lastName;
private int age;
private Address address;
}
public class Address {
private String streetAddress, city, state;
private int postalCode;
}
new Gson().fromJson(json, Employee.class);, where json is the raw JSON string, should then do what you want. Without sharing your code, it is hard to tell why it doesn't work for you.

Jackson in JSON Output: Rename Fields in Multiple Use Cases (without a single JsonProperty)

Is it possible to rename JSON output fields in an object an arbitrary number of times when outputting with Jackson?
I can use a one-time JsonProperty as shown here,
How to map JSON field names to different object field names?
But suppose I have a single class which is used in multiple outputs. In each output, I want to have the flexibility of defining which name(s) to change.
public class Study implements Serializable {
// Can vary as "id" / "studyId" depending on call
private int id;
// Can vary as "description" / "studyDescription" / "studyDesc" depending on call
private String description;
}
Or do I need to create new objects for each case?
Do refer: https://www.baeldung.com/json-multiple-fields-single-java-field
It's as simple as using #JsonAlias in combination with #JsonProperty annotation as below:
public class Study implements Serializable {
// Can vary as "id" / "studyId" depending on call
#JsonProperty("id")
#JsonAlias("studyId")
private int id;
// Can vary as "description" / "studyDescription" / "studyDesc" depending on call
private String description;
}
PS: Using #JsonProperty twice didn't work :D

Can I derive ARRAY_CONTAINS from query method name in Spring Data Couchbase Repository?

I have a Couchbase-Document "Group" with a list of group-members-names. I want to query for all groups of one person. I managed to do it with N1QL ARRAY_CONTAINS - see in code example - but i hoped that i could generate the query from the method name as it is usual in Spring Data.
Any help is appreciated :)
I tried
public List<MyGroup> findAllByMembers(String member); and public List<MyGroup> findByMembers(String member); but they just return an empty list - i guess they try to match the whole "members" value and don't recognize it as a list -, no errors.
Code
My Document with a List field
#Data
#Document
public class MyGroup {
private String name;
private List<String> members = new ArrayList<>();
}
My Repository
#RepositoryDefinition(domainClass = MyGroup.class, idClass = String.class)
public interface MyGroupRepository extends CouchbaseRepository<MyGroup, String> {
//#Query("#{#n1ql.selectEntity} WHERE ARRAY_CONTAINS(members,$1) AND #{#n1ql.filter}")
public List<MyGroup> findAllByMembers(String member);
}
Expected
Given a "group1" with "member1" in members.
repository.findAllByMembers("member1"); should return ["group1"].
Couchbase is limited by the Spring Data specification. Unfortunately, we can't simply add new behaviors to it (if you switch to a relational database, it has to work with no breaking points). So, whenever you need to query something that has a N1QL specific function/keyword, you have to write a query via #Query

Spring Boot MongoDB Persistance MappingException: Cannot convert Java.util.ArrayList into an instance of class java.lang.Object

tl;dr
Atempting to add an ArrayList in which Object may be an ArrayList to Persistance.
Tried to add an AttributeConverter > Failed
Plz Help
I have no idea what I am doing.
How stupid am I?
The Problem
Dependencies
spring-boot-starter-data-jpa 2.0.0
spring-boot-starter-data-mongodb 2.0.0
eclipselink 2.7.1 <- Probably don't need this one, not sure.
So here is my problem I am trying to add persistence in a Spring Boot Application for a MongoDB in this case I am using tables, the problem comes exactly on the TableRaw bean (a striped down version of Table just for persistance).
Document(collection = "rule_tables")
public class TableRaw {
#Id
private String _id;
private String key;
private String name;
private String returns;
private ArrayList<AxisRaw> axis;
private ArrayList<Object> values = new ArrayList<>();
}
Everything else is just the default constructor (without _id) and getsetters.
So everything works fine with the exception of the values ArrayList. It works fine if it just a simple ArrayList with number and whatnot however in my case I want something like what I am inserting into the database (this is done every time it runs for testing purposes and the values inserted are using the MongoRepository, it works fine)
{
"_id":"5ac20c8b8ee6e6360c8947be",
"key":"1",
"name":"Table 1",
"returns":"Number",
"axis":[
{
"name":"potato",
"values":[
{
"_id":"BottomEdge","value":0
},{
"_id":"Range",
"value":[1,2]
},{
"_id":"TopEdge",
"value":3
}
]
}
],
"values":[
[1,2,3],
[1,2,3],
[1,2,3]
],
"_class":"pt.i2s.gm.gm.rulehandler.tables.model.TableRaw"
}
(For usage in the code the axis length and number of axis matters but in this case it is completely irrelevant.)
Anyway as stated previously it inserts fine into MongoDB but when attempting to get the value the following error is presented.
org.springframework.data.mapping.MappingException: Cannot convert [1, 2, 3] of type class java.util.ArrayList into an instance of class java.lang.Object! Implement a custom Converter<class java.util.ArrayList, class java.lang.Object> and register it with the CustomConversions. Parent object was: [empty]
First thing first I don't exactly know what Parent object was: [empty] means.
Second I tried creating an AttributeConverter as such:
#Component
#Converter(autoApply = true)
public class ArrayList2ObjectConverter implements
AttributeConverter<ArrayList<Object>,Object> {
#Override
public Object convertToDatabaseColumn(ArrayList<Object> attribute) {
return attribute;
}
#SuppressWarnings("unchecked") //If you don't like it suppress it
#Override
public ArrayList<Object> convertToEntityAttribute(Object dbData) {
System.out.println("Converting...");
return (ArrayList<Object>)dbData;
}
}
And adding #Convert(converter = ArrayList2ObjectConverter.class) above the values attribute. However this wasn't even called.
For some reason I couldn't find any answers to this problem, possibly due to my bad coding and making something that is just stupid to do so nobody would do it like this cause it doesn't work.
So how do I do this? And thank you for reading.
Update regarding the Axis and Value amounts
thomi sugested something that would work if I knew from the get go what type of values the table added. I apreciate the answere however some clarification should be made regarding this.
I do not know how many Axis, and therefore nested arrays I will have, it may be 1 it may be 30.
I do not know what the class type of objects will be, it may be numbers, Strings, Booleans, dates, etc. the options are limited but still extensive.
Possible Solution Which I Do Not don't want to use
I could simply create an Object that held a string and an ArrayList which would probably work fine, however I wanted to avoid this resolution, as I don't want to add irrelevant information to the database.
Adopted Solution
By request of #user_531 I will add the solution to this problem.
As this was not working I altered my aproach to the utilization of a new object called ValueList which is simply a wrapper class for a single Object
private ArrayList<ValueList> values;
ValueList Class
public class ValueList {
public Object value;
}
This allows me to add any type of object I want to the list, this does result however in tables looking like this:
{
"key":1,
...... (Same as above)
"values": [
{
"value": [
{
"value":1
},
{
"value":2
}
]
},
{
"value": [
{
"value":3
},
{
"value":4
}
]
}
]
}
Which does look hidious but it doesn't fail anymore and allows me to read values relativelly consistently by calling the "getValue()" method or "getValueList()" method acording to the result from "isValueList()".
I think you should not map something to an object. In your DB, you will surely have an idea of what datatype there will be in your Array, In your case, try and replace with:
#Document(collection = "rule_tables")
public class TableRaw {
#Id
private String _id;
private String key;
private String name;
private String returns;
private ArrayList<AxisRaw> axis;
private List<List<Integer>> values; // no initialization.
}
This should map your structure just fine.

JPA: Use String fields, but modify them and save as byte array

we are using JSON String in our application to store a lot of configuration data. Now we want to save this to a database as BLOB. The JSON String will be converted to a binary representation (BSON) and then we want to store this.
Entity:
#Entity
#Table(name="TBL_CONFIG")
public class ConfigEntity {
[...]
#Column(name = "CONFIG")
#JSON
private String config;
[...]
}
Global EntityListener:
public class JsonBinaryConversionListener {
#PrePersist
public void process() {
// Check #JSON fields and for every field
[...]
field.set(objectConversion.toBson(field.get()));
[...]
// The field is String, the conversion gives us a byte array
}
}
The column CONFIG is set as BLOB. Only use the #Lob annotation wont work, because we want to change the value manually.
Is there a way that we can realize this through JPA?
If you are using EclipseLink you can use a Converter,
see,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_converter.htm#CHDEHJEB
Otherwise you can use property access (get/set) methods to convert the data,
http://en.wikibooks.org/wiki/Java_Persistence/Basic_Attributes#Conversion
The JPA 2.1 draft has also proposed Converter support.

Categories

Resources