Jackson-CSV schema for array - java

I have CSV file which I need to parse. The schema for this file is following:
name, contacts - where contacts are many strings for each person where
the number of this contact columns is not regular.
For example:
john, john#wick.com, 123 123 123, fb/john.wick
mike, 123 0303 11
dave,
I'm trying to create a CsvSchema with Jacskon CSV for my bean:
public class Person {
private String name;
private String[] contacts;
}
By creating custom schema:
CsvSchema schema = CsvSchema.builder()
.addColumn("name")
.addArrayColumn("contacts", ",")
.build();
But I am getting this:
com.fasterxml.jackson.dataformat.csv.CsvMappingException: Too many entries: expected at most 2
How to with Jackson CSV solve problem like that?
Java code:
CsvMapper mapper = new CsvMapper();
CsvSchema schema = CsvSchema.builder()
.addColumn("name")
.addArrayColumn("contacts", ",")
.build();
MappingIterator<Person> it = mapper.readerFor(Person.class).with(schema)
.readValues(csvString);
List<Person> all = it.readAll();

You can use CsvParser.Feature.WRAP_AS_ARRAY feature and read whole row as List<String>. In constructor you can convert List to Person object. See below example:
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvParser;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
public class CsvApp {
public static void main(String[] args) throws Exception {
File csvFile = new File("./resource/test.csv").getAbsoluteFile();
CsvMapper csvMapper = new CsvMapper();
csvMapper.enable(CsvParser.Feature.WRAP_AS_ARRAY);
MappingIterator<List<String>> rows = csvMapper.readerFor(List.class).readValues(csvFile);
List<Person> persons = rows.readAll().stream()
.filter(row -> !row.isEmpty())
.map(Person::new)
.collect(Collectors.toList());
persons.forEach(System.out::println);
}
}
class Person {
private String name;
private List<String> contacts;
public Person(List<String> row) {
this.name = row.remove(0);
this.contacts = row;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getContacts() {
return contacts;
}
public void setContacts(List<String> contacts) {
this.contacts = contacts;
}
#Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", contacts=" + contacts +
'}';
}
}
For your input above code prints:
Person{name='john', contacts=[john#wick.com, 123 123 123, fb/john.wick]}
Person{name='mike', contacts=[123 0303 11]}
Person{name='dave', contacts=[]}

I think the problem is with the column separator being the same as the array column separator.
You can use ; instead of , in your CSV.
Should be: john,john#wick.com;123 123 123;fb/john.wick
This way you can continue to use the features of Jackson instead of having to manually instantiate from a List of Strings.

Related

How to map key and values from two files?

I have searched around but couldn't find any similar question so I am posting a new one.
Currently I have basically two files. One file is the definition of the key, another file is the value that maps to the key (CSV format, but I'd like to not restrict it CSV file).
File 1:
id:TEXT,name:TEXT,city:TEXT,state:TEXT,zip:Integer
What it means is that the file has 5 fields, it defines the id of type TEXT, name of type TEXT, zip of type Integer, etc.
File 2 (each record is separated by a new line, there will be thousands of lines record):
11212, karen, new york, NY, 10000
21312, jim, boston, MA, 10000
12312,,seattle,,10000 // name and state is not available in this record
So the file 2 will have the value that maps to the key in file 1, notice if the value is null or empty, it will just be ignored in the result.
What would be an element way to convert these files into a java object as below:
#Data
#AllArgsConstructor
public class RecordCollection {
// Key is to map to the `id`, whereas the rest of the values map to Record
Map<String, Record> records;
}
#Data
#AllArgsConstructor
public class Record {
String name;
String city;
String state;
Integer zip;
}
To start I have:
String keyValues = "id:TEXT,name:TEXT,city:TEXT,state:TEXT,zip:Integer";
Now I have the inputStream parsed for file 2 and here is where I am at:
BufferedReader file2InputStreamBuffered = new BufferedReader("file 2");
Now, how to map the value to my Java objects in an elegant way? (With 3rd party tools or any common libs)
You need to build CsvSchema for your format file and after that use it for reading CSV file. Code could look like below:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
public class CsvApp {
public static void main(String[] args) throws Exception {
File formatFile = new File("./resource/test.format").getAbsoluteFile();
File csvFile = new File("./resource/test.csv").getAbsoluteFile();
CsvSchema schema = createSchemaForFormat(formatFile);
CsvMapper csvMapper = new CsvMapper();
MappingIterator<Record> rows = csvMapper.readerFor(Record.class).with(schema).readValues(csvFile);
RecordCollection recordCollection = new RecordCollection(100);
while (rows.hasNext()) {
recordCollection.add(rows.next());
}
recordCollection.getRecords().forEach((k, v) -> {
System.out.println(k + " => " + v);
});
}
private static CsvSchema createSchemaForFormat(File formatFile) throws IOException {
String content = String.join("", Files.readAllLines(formatFile.toPath()));
String[] columns = content.split(",");
CsvSchema.Builder builder = CsvSchema.builder();
for (String column : columns) {
String[] columnData = column.split(":");
String name = columnData[0];
String type = columnData[1];
builder.addColumn(name, "Integer".equalsIgnoreCase(type) ? CsvSchema.ColumnType.NUMBER : CsvSchema.ColumnType.STRING);
}
return builder.build();
}
}
class RecordCollection {
private final Map<String, Record> records;
RecordCollection(int expectedSize) {
this.records = new HashMap<>(expectedSize);
}
public void add(Record record) {
this.records.put(record.getId(), record);
}
public Map<String, Record> getRecords() {
return records;
}
}
class Record {
private final String id;
private final String name;
private final String city;
private final String state;
private final Integer zip;
#JsonCreator
public Record(
#JsonProperty("id") String id,
#JsonProperty("name") String name,
#JsonProperty("city") String city,
#JsonProperty("state") String state,
#JsonProperty("zip") Integer zip) {
this.id = id;
this.name = name;
this.city = city;
this.state = state;
this.zip = zip;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public Integer getZip() {
return zip;
}
#Override
public String toString() {
return "Record{" +
"name='" + name + '\'' +
", city='" + city + '\'' +
", state='" + state + '\'' +
", zip=" + zip +
'}';
}
}
Above code prints:
12312 => Record{name=' ', city='seattle ', state=' ', zip=10000}
21312 => Record{name='jim ', city='boston ', state='MA', zip=10000}
11212 => Record{name='karen', city='new york', state='NY', zip=10000}
In above case Record class is tied with configuration file which defines columns. If format of CSV is not dynamic you can build schema in Java class without reading it from file. If it is dynamic, instead of Record class you could store Map<String, Object> type which could handle dynamic columns.
For more information take a look od Jackson CSV documentation.

How to dynamically assign headers to a csv file using CsvMapper in Java

Can anyone help please?
I am stuck on reading a csv file and serializing it onto a POJO.
I am using CsvMapper from jackson library. The reading and serialization part are done and works fine-ish. The issue is when the user moves the headers/columns around causing the serialization to make some alphabetical assumption that the values on the CSV file are also alphabetically.
Eg (File below has headers on the first row and the second row has person details values)
personNameHeader,personAgeHeader
Wiliam,32
Now my POJO is as follow
#JsonIgnoreProperties(ignoreUnknown = true)
// #JsonPropertyOrder(value = {"personNameHeader", "personAgeHeader" })
public class PersonDetailsCSVTemplate {
#JsonProperty("personNameHeader")
private String name;
#JsonProperty("personAgeHeader")
private String age;
//Public constructor and getters and setters...
This is the code to read the values from the CSV and map onto the class
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
...
CsvMapper csvMapper = new CsvMapper();
CsvSchema schema = csvMapper.typedSchemaFor(PersonDetailsCSVTemplate.class).withHeader();
MappingIterator<PersonDetailsCSVTemplate > dataIterator = csvMapper.readerFor(PersonDetailsCSVTemplate.class).with(schema)
.readValues(data);
while (dataIterator.hasNextValue()) {
PersonDetailsCSVTemplate dataCSV = dataIterator.nextValue();
}
After the serialization it can be seen that CsvMapper mapped the following:
PersonDetailsCSVTemplate.name = "32" andPersonDetailsCSVTemplate.age = "Wiliam"
By annotating the class with #JsonPropertyOrder(value = {"personNameHeader", "personAgeHeader" }) forces the CSV to be always name column followed by age column which isnt ideal.
Can anyone suggest anything that they think will work?
Regards
Since Jackson 2.7, you can use withColumnReordering(true) instead of sortedBy()
CsvSchema schema = csvMapper
.typedSchemaFor(PersonDetailsCSVTemplate.class)
.withHeader()
.withColumnReordering(true);
You could use sortedBy and give it the order in which the properties apeare in your csv data:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonPropertyOrder(value = { "personNameHeader", "personAgeHeader" })
static class PersonDetailsCSVTemplate
{
#JsonProperty("personNameHeader")
private String name;
#JsonProperty("personAgeHeader")
private String age;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAge()
{
return age;
}
public void setAge(String age)
{
this.age = age;
}
#Override
public String toString()
{
return "PersonDetailsCSVTemplate [name=" + name + ", age=" + age + "]";
}
}
You can keep or leave #JsonPropertyOrder it won't affect the output.
#Test
public void sort() throws IOException
{
CsvMapper csvMapper = new CsvMapper();
CsvSchema schema = csvMapper
.typedSchemaFor(PersonDetailsCSVTemplate.class)
.withHeader()
.sortedBy("personNameHeader", "personAgeHeader")
.withColumnSeparator(',')
.withComments();
MappingIterator<PersonDetailsCSVTemplate> dataIterator =
csvMapper
.readerFor(PersonDetailsCSVTemplate.class)
.with(schema)
.readValues("personNameHeader,personAgeHeader\r\n"
+
"Wiliam,32\r\n");
while (dataIterator.hasNextValue())
{
PersonDetailsCSVTemplate dataCSV = dataIterator.nextValue();
System.out.println(dataCSV);
}
}
Output:
PersonDetailsCSVTemplate [name=Wiliam, age=32]

Parsing JSON data into model objects in Java

I haven't worked with JSON data before, thus the question.
I've the following JSON object in a file.
{
"courses": [
{ "id":998", "name":"Java Data Structures", "teacherId":"375" },
{ "id":"999", "name":"Java Generics", "teacherId":"376" }
],
"teachers": [
{ "id":"375", "firstName":"Amiyo", "lastName":"Bagchi"},
{ "id":"376", "firstName":"Dennis", "lastName":"Ritchie"}
]
}
Here are my model Objects.
public class Course {
private int _id;
private String _name;
private Teacher _teacher;
}
public class Teacher {
private int _id;
private String _firstName;
private String _lastName;
}
My task is to read the JSON Objects and return a list of Model objects.
I've imported the simple.JSON family of jar and here's my code that reads the file.
FileReader reader = new FileReader(path);
JSONParser parser = new JSONParser();
Object obj = parser.parse(reader);
JSONObject jsonObject = (JSONObject) obj;
My question is,
How do I parse the JSON document into my Model objects?
If the input file is JSON but of a different format how do I throw exception/handle the anomaly?
Any help appreciated.
UPDATE I suggest you use JSON parser to parse the data:
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
class Course {
public int _id;
public String _name;
public Teacher _teacher;
private Course(int id, String name, Teacher teacher){
this._id = id;
this._name = name;
this._teacher = teacher;
}
public Course() {
}
}
class Teacher {
public int _id;
public String _firstName;
public String _lastName;
private Teacher(int id, String fname, String lname){
this._id = id;
this._firstName = fname;
this._lastName = lname;
}
public Teacher(){
}
}
public class jsontest {
public static void main(String[] args) throws JSONException, IOException {
// String JSON_DATA = "{\n"+
// " \"courses\": [\n"+
// " { \"id\":\"998\", \"name\":\"Java Data Structures\", \"teacherId\":\"375\" },\n"+
// " { \"id\":\"999\", \"name\":\"Java Generics\", \"teacherId\":\"376\" }\n"+
// "\n"+
// " ],\n"+
// " \"teachers\": [\n"+
// " { \"id\":\"375\", \"firstName\":\"Amiyo\", \"lastName\":\"Bagchi\"},\n"+
// " { \"id\":\"376\", \"firstName\":\"Dennis\", \"lastName\":\"Ritchie\"} \n"+
// " ]\n"+
// "}\n"+
// "";
// read json file into string
String JSON_DATA = new String(Files.readAllBytes(Paths.get("path_to_json_file")), StandardCharsets.UTF_8);
// using a JSON parser
JSONObject obj = new JSONObject(JSON_DATA);
// parse "teachers" first
List<Teacher> listCourses = new ArrayList<Teacher>();
List<JSONObject> listObjs = parseJsonData(obj,"teachers");
for (JSONObject c: listObjs) {
Teacher teacher = new Teacher();
teacher._id = c.getInt("id");
teacher._firstName = c.getString("firstName");
teacher._lastName = c.getString("lastName");
listCourses.add(teacher);
}
// parse "courses" next
List<Course> resultCourses = new ArrayList<Course>();
List<JSONObject> listObjs2 = parseJsonData(obj, "courses");
for (JSONObject c: listObjs2) {
Course course = new Course();
course._id = c.getInt("id");
course._name = c.getString("name");
int teacherId = c.getInt("teacherId");
HashMap<String, Teacher> map = new HashMap<String, Teacher>();
for (Teacher t: listCourses){
map.put(Integer.toString(t._id), t);
}
course._teacher = map.get(Integer.toString(teacherId));
resultCourses.add(course);
}
}
public static List<JSONObject> parseJsonData(JSONObject obj, String pattern)throws JSONException {
List<JSONObject> listObjs = new ArrayList<JSONObject>();
JSONArray geodata = obj.getJSONArray (pattern);
for (int i = 0; i < geodata.length(); ++i) {
final JSONObject site = geodata.getJSONObject(i);
listObjs.add(site);
}
return listObjs;
}
}
Output:
BTW: The json data in the example has one value whose double quotes are not in pairs. To proceed, it must be fixed.
You should try using Jackson as the JSON parsing library instead. There is a lot more support and features that come with it.
In your case, a couple of annotations to map the JSON properties to the Java fields should be sufficient.
https://github.com/FasterXML/jackson-annotations
https://github.com/FasterXML/jackson-databind
UPDATE: Some code, to show just much better this can be done with Jackson.
public class Course {
#JsonProperty("id")
private int _id;
#JsonProperty("name")
private String _name;
#JsonProperty("teacher")
private Teacher _teacher;
// ...public getters and setters
}
public class Teacher {
#JsonProperty("id")
private int _id;
#JsonProperty("firstName")
private String _firstName;
#JsonProperty("lastName")
private String _lastName;
// ...public getters and setters
}
// Container class to conform to JSON structure
public class CoursesDto {
private List<Teacher> teachers;
private List<Course> courses;
}
// In your parser place
ObjectMapper mapper = new ObjectMapper();
FileReader reader = new FileReader(path);
CoursesDto dto = mapper.readValue(reader, CoursesDto.class);
The #JsonProperty annotations tell Jackson what JSON key should be used to deserialize. They are not necessary if the property names match the JSON keys. That means that if you remove the leading underscore from your property names, this would work without annotations. Also, Jackson will default to using public fields and getter/setter methods. This means that you can keep your fields prefixed by _ as long as the getter/setter don't have it (setFirstName(String firstName)).

Creating JSON using Jackson

I have a List<Person> list.
I want to serialize it to JSON with some other attributes, using Jackson Api.
The output should be like this:
{
"sEcho": 3,
"iTotalRecords": 10,
"iTotalDisplayRecords": 10,
"aaData": [ <--here's the persons
{
"name": "TestName1",
"status": "ok"
},
{
"name": "TestName2",
"status": "ok"
},
...
]
}
Probably very simple but couldn't figure it from Jackson's Api.
Thanks
I'd create a new class called PersonGroup having any of the extra fields you need with the List as another field on this class - for the example you gave this field would be named aaData.
This would represent the structure you have here. If you think a new class is too much then you could just make a HashMap of Objects and store the extra fields as whatever object you like and then add these to the HashMap, making sure the keys match the name of the extra fields and making sure your List is also in the HashMap.
Deserializing this class or HashMap should return the output you mentioned.
Try with this.
Hope it would help.
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class JsonSimpleExample {
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
for(int i=0 ;i< 5;i ++) {
Person person = new Person("TestName" + (i+1), "OK");
personList.add(person);
}
JSONObject obj = new JSONObject();
obj.put("sEcho", 3);
obj.put("iTotalRecords", 10);
obj.put("iTotalDisplayRecords", 10);
JSONArray list = new JSONArray();
for (Person person : personList) {
JSONObject innerObj = new JSONObject();
innerObj.put("name",person.getName());
innerObj.put("status",person.getStatus());
list.add(innerObj);
}
obj.put("aaData", list);
System.out.print(obj);
}
}
class Person {
private String name;
private String status;
public Person(String name, String status) {
this.name = name;
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

Inserting Java Object to MongoDB Collection Using Java

I am trying to insert a whole Java object into a MongoDB Collection using Java. I am getting following error:
Error :
Exception in thread "main" java.lang.IllegalArgumentException: can't serialize class net.yogesh.test.Employee
at org.bson.BSONEncoder._putObjectField(BSONEncoder.java:185)
at org.bson.BSONEncoder.putObject(BSONEncoder.java:119)
at org.bson.BSONEncoder.putObject(BSONEncoder.java:65)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:176)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:134)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:129)
at com.mongodb.DBCollection.save(DBCollection.java:418)
at net.yogesh.test.test.main(test.java:31)
Emplyoee.java (POJO)
package net.yogesh.test;
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private long no;
private String name;
public Employee() {
}
public long getNo() {
return no;
}
public void setNo(long no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main Method Class (test.java)
package net.yogesh.test;
import java.net.UnknownHostException;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
public class test {
public static void main(String[] args) throws UnknownHostException,
MongoException {
Mongo mongo = new Mongo("localhost", 27017);
DB db = mongo.getDB("test");
Employee employee = new Employee();
employee.setNo(1L);
employee.setName("yogesh");
BasicDBObject basicDBObject = new BasicDBObject("Name", employee);
DBCollection dbCollection = db.getCollection("NameColl");
dbCollection.save(basicDBObject);
}
}
Can anybody explain why I am getting this error?
I'm a little confused as to know why you'd think this would work in the first place. The first thing you need to know is how to map your POJO to a MongoDB document. Currently, you're not telling the system(your code) how to do that.
You can either use a mapping library for this (Morphia comes to mind) or use ReflectionDBObject. Either solution allows you to map POJO to MongoDB document or MongoDB document to POJO(the former way is a lot more nicely than the latter).
DB db = mongoClient.getDB( "mydb" );
coll = db.getCollection("testCollection");
Employee emp = new Employee();
emp.setId("1001");
emp.setName("John Doe");
//Converting a custom Class(Employee) to BasicDBObject
Gson gson = new Gson();
BasicDBObject obj = (BasicDBObject)JSON.parse(gson.toJson(emp));
coll.insert(obj);
findEmployee(new BasicDBObject("id","1001"));
public static void findEmployee(BasicDBObject query){
DBCursor cursor = coll.find(query);
try {
while(cursor.hasNext()) {
DBObject dbobj = cursor.next();
//Converting BasicDBObject to a custom Class(Employee)
Employee emp = (new Gson()).fromJson(dbobj.toString(), Employee.class);
System.out.println(emp.getName());
}
} finally {
cursor.close();
}
}
I thought that it would be useful to post code that did conversions both ways.
Storing an Employee Object
Finding and re-creating an employee Object
Hope this is useful..
Pro
you continue to work with strong typed objects as you wanted to
Contra
Some people really dislike : extends
package foo;
import com.mongodb.BasicDBObject;
public class Employee extends BasicDBObject {
private static final long serialVersionUID = 2105061907470199595L;
//should be something shorter as "name" like "n"
//here just use name to conform your sample
public static final String NAME = "name";
public static final String NO = "no";
public static final String COLLECTION_NAME = "employee";
public Long getNo() {
return getLong(NO);
}
public void setNo(long no) {
put(NO, no);
}
public String getName() {
return getString(NAME);
}
public void setName(String name) {
put(NAME, name);
}
}
package foo;
import java.net.UnknownHostException;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
public class Test {
public static void main(String[] args) throws UnknownHostException,
MongoException {
Mongo mongo = new Mongo("localhost", 27017);
DB db = mongo.getDB("yeahMongo");
Employee employee = new Employee();
employee.setNo(1L);
employee.setName("yogesh");
DBCollection employeeCollection = null ;
employeeCollection = db.getCollection(Employee.COLLECTION_NAME);
employeeCollection.save(employee);
System.err.println(employeeCollection.findOne());
}
}
In addition to morphia you should take a look to
jongo :
http://jongo.org/
jongo use the same form syntax as js mongo engine, and I found it great point for a beginner. You don't have to switch your mental map between mongojs and java. you can use the js sample with little changes.
You can convert your java object into json string using the gson library and then insert it in mongodb.
Eg:
Gson gson = new Gson();
String json = gson.toJson(Employee);
BasicDBObject basicDBObject = new BasicDBObject("Name", json );
DBCollection dbCollection = db.getCollection("NameColl");
dbCollection.save(basicDBObject);
There have been several changes since this question was asked. Using test.java in the question, here is what worked for me using Google's Gson:
import com.google.gson.Gson;
import com.mongodb.Block;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
public class test {
public static void main(String[] args) {
MongoClient mongoClient = new MongoClient(); // Connect with default settings i.e. localhost:27017
MongoDatabase db = mongoClient.getDatabase("test"); // Get database "test". Creates one if it doesn't exist
Employee employee = new Employee(); // Create java object
employee.setNo(1L);
employee.setName("yogesh");
// Deserialize object to json string
Gson gson = new Gson();
String json = gson.toJson(employee);
// Parse to bson document and insert
Document doc = Document.parse(json);
db.getCollection("NameColl").insertOne(doc);
// Retrieve to ensure object was inserted
FindIterable<Document> iterable = db.getCollection("NameColl").find();
iterable.forEach(new Block<Document>() {
#Override
public void apply(final Document document) {
System.out.println(document); // See below to convert document back to Employee
}
});
}
}
You can also use Gson to convert retrieved bson document back to Java object:
Gson gson = new Gson();
Employee emp = gson.fromJson(document.toJson(), Employee.class);
With MongoDB you cannot insert your Java bean in the DB, but you have to remap them to MongoDB Object.
In your case you have to do:
BasicDBObject basicDBObject = new BasicDBObject();
basicDBObject.put("no", employee.getNo());
basicDBObject.put("name", employee.getName());
Hopefully this will work for you and you can get help from it.
I performed database operations(insert,delete, update, get, getall) and used Person object for operations in MongoDB with java for demo purpose.
Database connection class
Connection.java
package test;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.client.MongoDatabase;
public class Connection {
public MongoClient mongo;
private String db;
public MongoDatabase database;
private static Connection instance;
private Connection() {
db = "chatsystem";
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()));
mongo = new MongoClient("localhost", MongoClientOptions.builder().codecRegistry(pojoCodecRegistry).build());
database = mongo.getDatabase(db);
}
public static Connection getInstance() {
if (instance == null) {
instance = new Connection();
return instance;
} else {
return instance;
}
}
}
Model Class
Person.java
package test;
import org.bson.types.ObjectId;
public class Person {
public Person() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
private ObjectId id;
public Person(String username, String email, String password, String fname, String lname) {
super();
this.username = username;
this.email = email;
this.password = password;
this.fname = fname;
this.lname = lname;
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
private String username;
private String email;
private String password;
private String fname;
private String lname;
}
Main class
test.java
package test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static com.mongodb.client.model.Filters.*;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
public class test {
private MongoCollection<Person> person;
Connection conn;
public void getCollection() {
conn = Connection.getInstance();
person = conn.database.getCollection("person", Person.class);
}
public static void main(String[] args) throws Exception {
test t = new test();
t.getCollection();
Person p = new Person();
p.setEmail("test#test.com");
p.setFname("ftest");
p.setLname("ltest");
p.setPassword("testtest");
p.setUsername("test123");
// insert person type objects in database
t.insertPerson(p);
// get all persons from database
List<Person> pp = t.getAllPersons();
Person pt = pp.get(0);
System.out.println(pt.getEmail());
System.out.println(pt.getId());
// get one person from database by username filter
// pass username of person in method argument
Person ph = t.getOnePerson("test123");
System.out.println(ph.getEmail());
System.out.println(ph.getId());
// update/edit person by username filter
// pass username of person in method argument
t.updatePerson("test123");
// delete person by username filter
// pass username of person in method argument
t.removePerson("updatetest123");
}
public void insertPerson(Person p) {
person.insertOne(p);
}
public List<Person> getAllPersons() {
FindIterable<Person> iterable = person.find();
Iterator it = iterable.iterator();
List<Person> allPersons = new ArrayList<>();
while (it.hasNext()) {
Person per = (Person) it.next();
allPersons.add(per);
}
return allPersons;
}
public Person getOnePerson(String username) {
return person.find(eq("username", username)).first();
}
public void updatePerson(String username) {
Person p = new Person();
p.setEmail("update#test.com");
p.setFname("updateftest");
p.setLname("updateltest");
p.setPassword("updatetesttest");
p.setUsername("updatetest123");
person.replaceOne(eq("username", username), p);
}
public void removePerson(String username) {
person.deleteOne(eq("username", username));
}
}
Highly recommend MongoJack, a decent library to map Java objects to/from MongoDB documents.
The code would be something like below:
import java.util.Arrays;
import org.mongojack.JacksonDBCollection;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.ServerAddress;
public class Test {
public static void main(String[] args) {
MongoClient mongoClient = new MongoClient(Arrays.asList(new ServerAddress("localhost", 27017)));
DB db = mongoClient.getDB("test");
Employee employee = new Employee();
employee.setNo(1L);
employee.setName("yogesh");
JacksonDBCollection<Employee, String> collectionData = JacksonDBCollection.wrap(db.getCollection("NameColl"), Employee.class, String.class);
collectionData.save(employee);
mongoClient.close();
}
}
(PS: Currently I'm using mongo-java-driver v3.2.2, and mongojack v2.6.1)
Use BasicDBObjectBuilder to convert your POJO to an instance of DBObject which a DBCollection can save:
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
public class Employee {
private long no;
private String name;
// Getters and Setters
public DBObject toDBObject() {
BasicDBObjectBuilder builder = BasicDBObjectBuilder
.start("no", no)
.append("name", name);
return builder.get();
}
}
In order to save, just call toDBObject() on POJO instance and provide it to collection:
public class test {
public static void main(String[] args) throws UnknownHostException,
MongoException {
...
DBCollection dbCollection = db.getCollection("NameColl");
Employee employee = new Employee();
employee.setNo(1L);
employee.setName("yogesh");
dbCollection.save(employee.toDBObject());
}
}
Using this approach:
You don't need to manually create a DBObject every time
You don't need to mess up your POJO by extending Mongo classes (what if your POJO is already extending a class?)
You don't need a Json mapper [and its annotations on POJO fields]
You only have dependency to java-mongo-driver jar
I have the same error, when I try to insert a java BasicDBObject into a MongoDb Collection.
My object is created from a Xml converted to Json.
java.lang.IllegalArgumentException: can't serialize class net.sf.json.JSONNull
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:299)
at org.bson.BasicBSONEncoder.putMap(BasicBSONEncoder.java:339)
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:261)
This error is caused by empty tags in Xml; when I removed all empty tags, then I solved it.
Just use "insertOne" method, not save.
MongoCollection collection;
String collectionName = "somename";
String jsonObject = "{}";
if (!mongoTemplate.collectionExists(collectionName)) {
collection = mongoTemplate.createCollection(collectionName);
logger.info("Collection %s was successfully created", collectionName);
} else {
collection = mongoTemplate.getCollection(collectionName);
}
collection.insertOne(Document.parse(jsonObject));
Since nobody has mentioned it - I think there might be a solution to this using bson4jackson. This bills itself as a fast encoder.

Categories

Resources