0

This is for all the JSON brains out there.

Let's say I have an interface like:

public interface Model {
  String getName();
  String getDateCreated();
  String getTimeCreated();
  String getLastUpdated();
  String getLastUpdatedTime();
}

Then let's say I have an Item object:

public class Item {

  private String name;
  private Date created;
  private Date updated;

  ..setters and getters
}

Then let's say I have an model wrapper:

public class ItemModel implements Model {

  private final Item item;

  public ItemModel(Item item){
      this.item = item;
  }

  public String getName(){
      return item.getName();
  }

  public String getDateCreated(){
      Date created = item.getCreated();
      //format the date nicely and return a date string
  }

  public String getTimeCreated(){
      Date created = item.getCreated();
      //format the date nicely and return a time string
  }

  ...you get the gyst, right?
}

Question is:

  1. How do I serialize ItemModel so that the json output will reflect the getter names on the model interface?
  2. Specifically, which library does this most easily? How do you achieve this using a specific library?
  3. Finally, I DO NOT want to serialize the Item object wrapped inside the ItemModel

I want the output to be something like this:

{item : {name: '', dateCreated : '', timeCreated : '', lastUpdated : '', lastUpdatedTime : ''}

Thanks in advance!


I thought I should share what I have been able to come with so far, and get your feedback. It's almost meeting my requirements, but I'm not sure if it's the right way:

I was pointed to genson by a user here, and I find it very interesting.

First I created a Serializer

public class ModelSerializer implements Serializer {

@Override
public void serialize(T target, ObjectWriter writer, Context cntxt) throws TransformationException, IOException {
    writer.beginObject();
    for (Method method : target.getClass().getMethods()) {
        String methodName = method.getName();
        if (methodName.startsWith("get")) {
            int index = "get".length();
            String valueName = Character.toLowerCase(methodName.charAt(index)) + method.getName().substring(index + 1);
            try {
                String valueString = (String) method.invoke(target, new Object[]{});
                writer.writeName(valueName).writeValue(valueString);
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(ModelSerializer.class.getName()).log(Level.SEVERE, null, ex);
                throw new TransformationException(ex.getMessage());
            } catch (IllegalAccessException ex) {
                Logger.getLogger(ModelSerializer.class.getName()).log(Level.SEVERE, null, ex);
                throw new TransformationException(ex.getMessage());
            } catch (InvocationTargetException ex) {
                Logger.getLogger(ModelSerializer.class.getName()).log(Level.SEVERE, null, ex);
                throw new TransformationException(ex.getMessage());
            }
        }
    }
    writer.endObject();
}

}

Next, I created Genson and passed the Serializer

Genson genson = new Genson.Builder().withSerializer(new ModelSerializer(), Model.class).create();

Now when I pass the ViewModel, I get the fields formatted as I expected

Model viewModel = new ItemModel(domainModel); where domainModel is an instance of Item System.out.println(genson.serialize(viewModel));

mainas
  • 754
  • 1
  • 6
  • 15
  • Great that you like Genson! But why are you doing all the introspection stuff? What is the exact output you are expecting? You should define your Serializer as ModelSerializer implements Serializer { ... class code ... } and then just register it with Builder().withConverters(new ModelSerializer()).create(); – eugen Oct 04 '12 at 19:22
  • Why don't you just do new Genson().serialize(viewModel); ? You will get the same output as with your custom serializer (I jus tested both solutions). – eugen Oct 04 '12 at 19:31
  • Did you notice that the Model interface returns string values only, while Item return strings and dates? You have to do some formatting for the date output, and this was causing the problem. – mainas Oct 05 '12 at 20:50
  • Yes I have noticed, but by putting @JsonIgnore on item he will not be serialized and only the get methods from ItemModel will be used (with the formatting you defined inside those methods). Does it solve your problem if you do it this way? – eugen Oct 05 '12 at 20:56
  • Ah and you can define the formatting that you want genson to use in serializing dates by doing: Genson genson = new Genson.Builder().setDateFormat(yourDateFormat).create(); – eugen Oct 05 '12 at 21:22
  • Ah and if you have a getItem method in your ItemModel you should annotate it with @JsonIgnore – eugen Oct 05 '12 at 21:42

3 Answers3

0

I would refactor your code a bit. First you want a POCO class as your data carrier, which would be your Item Class. Then you would want a Repository to do the CRUD operations for you.

public interface IItemModelRepository {
 Item getItemByName();
 Item getItemByDateCreated();
}

You would implement the interface as:

public class ItemModelRepository : IItemModelRepository {

private IEnumerable<Item> _items;

public ItemModelRepository()
{
   this._items = /// Logic to load all items from database or from in memory cache
} 

public Item getItemByName(string name){
  return this._items.First(i => i.Name.Equals(name);
}

public Item getItemByDateCreated(DateTime dateCreated){
  return this._items.First(i => i.DateCreated == dateCreated);     
}

....
}

Then you can use this Repository (not a good idea to use Repository directly in Controller. You would rather want to return a ViewModel, which is a POCO class containing the properties required by your view) as:

var item = ItemModelRespository.GetByName("enter name here");
var viewModel = new ViewModel();
var serializer = new JavaScriptSerializer();
viewModel.JsonData = serializer.Serialize(item );

And on client side, you can use:

<%= Model.JsonData %>

have a look here as well: ASP.NET MVC: How to convert View Model into Json object

--edit

Sorry, missed the bit that you are using Java. As mentioned here:

http://flax.ie/flax-engine-gwt-client-side-json-serialization-and-deserialization/

you could just modify your existing Item Class as:

public class Item implements JsonSerializable {

  private String name;
  private Date created;
  private Date updated;

 ..setters and getters

/**
* Pass this method JSON and it gives you back an object which you can
* then assign to your object via MyObject x = JsonToObject(String Json);
*
* @param JSON
* @return
*/
public static Item JsonToObject(String Json) {
    Serializer serializer = (Serializer) GWT.create(Serializer.class);
    return (Item)serializer.deSerialize(Json,"your fully qualified Item assembly name");
}

/**
* Creates a JSON string from the current object
*
* @return String of JSON
*/
public String toJson() {
    Serializer serializer = (Serializer) GWT.create(Serializer.class);
    return serializer.serialize(this);
}
}

And then in your code that consumes IItemModelRepository, you can use:

var item = ItemModelRepository.GetItemByName("name");
var jsonString = item.ToJson();

What I do not get is, why do you want to serialize ItemModel when you want the JSON output to be serialized representation of the Item object?

Community
  • 1
  • 1
tranceporter
  • 2,241
  • 1
  • 21
  • 23
  • Thanks for the response. Your solution is .NET-specific. I am looking for java-specific solution. The closest I have come to seeing a potential solution to my problem is with FlexJson transformers, but I still haven't quite got the hang of the 'Transformers' concept yet. – mainas Oct 04 '12 at 15:24
  • check my updated answer.. I am not clear on your requirements – tranceporter Oct 04 '12 at 15:35
  • Thanks again. To address you question first, I've authored a library called commentit (http://code.google.com/p/commentit/), which I'm giving a spin. I'm confronted with a situation whereby I want a common interface for ViewModel objects to decouple them from the domain (database) objects - that way they can evolve to suit the client requirements. I'm using a template framework on the server side to merge html with model object - it's easy to invoke the interface method here. I'm also using JSON for the client-side. I want to maintain the same interface for both JSON and template framework – mainas Oct 04 '12 at 15:47
  • When you say that you want to decouple ViewModel from Domain, do you plan to use some sort of mapping layer to flatten/project the domain object to ViewModel? – tranceporter Oct 04 '12 at 15:49
  • SO I want Item to remain as is. I want to wrap it inside an interface so that I may "filter" out what I want and do not want to be visible to presentation layer and client side. I want my template framework to only be concerned with what the client requires and not know what the database objects look like. I want to serialize this same model object (domain object wrapped inside a "scope-limiting" interface) to JSON when need be. That way I will only have to maintain only one set of ViewModel objects. I hope that gives you a better idea of my intentions with this – mainas Oct 04 '12 at 15:53
  • Your comment preceeding mine is absolutely correct. Then my comment that comes after it reflects your thoughts and sheds more light as to "why" – mainas Oct 04 '12 at 15:58
  • I seem to understand your problem. What you could do is create a BaseClass that implements the JsonSerializer mentioned above. All you viewmodels will inherit from BaseClass. You can use AutoMapper or ValueInjector to flatten/project your data from domain to viewmodel. Ideally, you would want to have 1 viewmodel per page/view. That is atleast how it is done in ASP.NET MVC. Does that solve your problem? – tranceporter Oct 04 '12 at 18:17
  • Absolutely!! You got the point. In the meantime, I was hacking around with genson having been pointed there by another user , and so far I have something that almost works as I want it to. – mainas Oct 04 '12 at 18:23
0

You could try genson library it provides a couple of ways to solve your problem http://code.google.com/p/genson/.

If the desired output is {name: '', dateCreated : '', timeCreated : '', lastUpdated : '', lastUpdatedTime : ''} without "item :" :

public class ItemModel implements Model {    
    @JsonIgnore private final Item item; 
    ....
}

new Genson().serialize(myItemModel);

If you want the "item" key you could also implement a custom Serializer or Converter like in the wiki.

eugen
  • 5,856
  • 2
  • 29
  • 26
0

You can define your own JSON mappings manually with a library like Jettison so you don't have to contort yourself with POJOS and POCOs and wrappers and other silly things. It really doesn't take much time to set up the mappings.

smcg
  • 3,195
  • 2
  • 30
  • 40