Can’t serialize class – MongoDB Illegal Argument Exception

While working on the array modifiers example MongoDb
, I was trying to use the $sort
modifier with $each and $push modifier
.In that example I added
an array of books to my original
document and was trying to sort it on the basis of one of the fields.As soon as I executed that method,i got our infamous IllegalArgumentException : Exception in thread “main” java.lang.IllegalArgumentException: can’t serialize class com.lotusmedia.mongo.models.Book (Can’t serialize class – MongoDB).

Complete Trace :

Exception in thread "main" java.lang.IllegalArgumentException: can't serialize class com.lotusmedia.mongo.models.Book
	at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:270)
	at org.bson.BasicBSONEncoder.putArray(BasicBSONEncoder.java:282)
	at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:242)
	at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:174)
	at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:226)
	at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:174)
	at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:226)
	at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:174)
	at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:120)
	at com.mongodb.DefaultDBEncoder.writeObject(DefaultDBEncoder.java:27)
	at com.mongodb.OutMessage.putObject(OutMessage.java:289)
	at com.mongodb.OutMessage.writeUpdate(OutMessage.java:175)
	at com.mongodb.OutMessage.update(OutMessage.java:62)
	at com.mongodb.DBApiLayer$MyCollection.update(DBApiLayer.java:347)
	at com.mongodb.DBCollection.update(DBCollection.java:177)
	at com.mongodb.DBCollection.update(DBCollection.java:208)
	at com.mongodb.DBCollection.update(DBCollection.java:220)
	at com.lotusmedia.mongo.ArrayModifiers.usingSortSliceEachUnderPushModifier(ArrayModifiers.java:111)
	at com.lotusmedia.mongo.ArrayModifiers.main(ArrayModifiers.java:124)

Below is the code for my book class and for $sort modifier :
Book.java:

package com.lotusmedia.mongo.models;

import java.io.Serializable;

public class Book implements Serializable{
	
	private String bookName;
	private Integer numOfPages;
	
	public Book(String bookName,Integer numOfPages){
		this.bookName=bookName;
		this.numOfPages=numOfPages;
	}	
	
	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public Integer getNumOfPages() {
		return numOfPages;
	}
	
	public void setNumOfPages(Integer numOfPages) {
		this.numOfPages = numOfPages;
	}

	public static Book[] createBookArray(){
		Book[] books = new Book[2];
		books[0]=new Book("Introduction Of Spring Framework",500);
		books[1]=new Book("Expert One-on-One J2EE Development without EJB",900);
		return books;
		
	}

}

Sort Function : This function is part of the example from the previous post.

private static void usingSortSliceEachUnderPushModifier(){
		DBCollection lotusCollection=MongoUtils.getCollection(LOTUS_COLLECTION);
		
		//search object
		DBObject searchObject =new BasicDBObject();
		searchObject.put("firstName", "rod");
		
		//Adding Books
		DBObject modifiedObject =new BasicDBObject();
		DBObject eachObject =new BasicDBObject();
		eachObject.put("$each", Book.createBookArray());
	    eachObject.put("$slice", -3);
	    eachObject.put("$sort", new BasicDBObject().append("numOfPages", 1));
	    
	    modifiedObject.put("$push", new BasicDBObject().append("authorOf", eachObject));
	    lotusCollection.update(searchObject, modifiedObject);		
	    MongoUtils.printDocument("firstName", "rod");
	    
	  //Recreating the collection and records
	    MongoUtils.dropAndRecreateCollection(LOTUS_COLLECTION);
		
	}

Now you might be wondering why I am getting this exception as I have already marked my java class as Serailizable by implementing the Serailizable marker interface ,but it was my bad that I assumed MongoDB also worked as other traditional SQL databases work.

Till now we are under impression that whenever we want to send a Java object over the network we need to implement this marker interface but in the case of MongoDB things are little bit different. Try to recollect from SQL world where you used to have a table corresponding to a Java entity and there must be some framework e.g JPA,Hibernate etc present to bind them together. In the case of MongoDB there is so no such table ,all you have a collection and that can contain documents of any shape and size.

You have to tell the Java mongo driver that you want to store this object as a document in the mongo database. Most crucial point is MongoDB only understands documents that it stores in the BSON format (internally) ,so the Java object that we are trying to save here should also represent a document.Now if we go by the conventional approach without using any external API and only trusting on MongoDB Java Driver then we can do this either by extending the BasicDBObject or implementing DBObject interface.

Now you can pick any of the approach both of them have their pros and cons.For this example I am going to extend my Book class with BasicDBObject.
After extending it from BasicDBObject and with some other changes ,my Book class will look like this.
Book.java :

package com.lotusmedia.mongo.models;

import com.mongodb.BasicDBObject;

public class Book extends BasicDBObject{
	
	public Book(String bookName,Integer numOfPages){
		super.put("bookName", bookName);
		super.put("numOfPages", numOfPages);
	}
		
	public String getBookName() {
		return (String) super.get("bookName");
	}

	public void setBookName(String bookName) {
		super.put("bookName", bookName);
	}

	public Integer getNumOfPages() {
		return  (Integer) super.get("numOfPages");
	}
	
	public void setNumOfPages(Integer numOfPages) {
		super.put("numOfPages", numOfPages);
	}

	public static Book[] createBookArray(){
		Book[] books = new Book[3];
		books[0]=new Book("Introduction Of Spring Framework",500);
		books[1]=new Book("Expert One-on-One J2EE Development without EJB",900);
		return books;
		
	}

}

Here,you will also notice that our setters and getters method have been changed to match the MongoDB requirements to set it as key-value pairs in our documents.
Now when i run my example again , this time it will sort all the books in a document in the increasing order of number of pages (notice the positive value), if you want to sort it in decreasing order you can pass the negative value.
Output:

{  
  "_id" : { "$oid" : "535d7399a2f84a24ef2f920d"} , 
  "firstName" : "rod" , 
  "lastName" : "johnson" , 
  "founder" : "Spring" , 
  "wiki" : "http://en.wikipedia.org/wiki/Rod_Johnson_%28programmer%29" , 
  "hits" : 45000 , 
  "authorOf" : [ 
                 { "bookName" : "Introduction Of Spring Framework" , "numOfPages" : 500} , 
                 { "bookName" : "Expert One-on-One J2EE Development without EJB" , "numOfPages" : 900}
			   ]
}

There are other solutions also that we can use to solve the above problem.

  1. Parsing it into a JSON object and then save
  2. Using external api like Morphia ,Jmongo etc

I personally find Morphia much easier to work with (as I am also learning),it will give you the same feeling that you are working with a relational database,only difference will be ,instead of using hibernate or jpa , you are using morphia.

Let'sConnect

Saurabh Jain

A Developer working on Enterprise applications ,Distributed Systems, Hadoop and BigData.This blog is about my experience working mostly on Java technologies ,NoSQL ,git , maven and Hadoop ecosystem.
Let'sConnect
Add Comment Register



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>