views:

1317

answers:

5

If I get the following json from a RESTful client, how do I elegantly unmarshal the java.util.Date? (Is it possible without providing (aka. hard-coding) the format, that's what I mean by elegantly...)

{
  "class": "url",
  "link": "http://www.empa.ch",
  "rating": 5,
  "lastcrawl" : "2009-06-04 16:53:26.706 CEST",
  "checksum" : "837261836712xxxkfjhds",
}
+6  A: 

The cleanest way is probably to register a custom DataBinder for possible date formats.

import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CustomDateBinder extends PropertyEditorSupport {

    private final List<String> formats;

    public CustomDateBinder(List formats) {
        List<String> formatList = new ArrayList<String>(formats.size());
        for (Object format : formats) {
            formatList.add(format.toString()); // Force String values (eg. for GStrings)
        }
        this.formats = Collections.unmodifiableList(formatList);
    }

    @Override
    public void setAsText(String s) throws IllegalArgumentException {
        if (s != null)
            for (String format : formats) {
                // Need to create the SimpleDateFormat every time, since it's not thead-safe
                SimpleDateFormat df = new SimpleDateFormat(format);
                try {
                    setValue(df.parse(s));
                    return;
                } catch (ParseException e) {
                    // Ignore
                }
            }
    }
}

You'd also need to implement a PropertyEditorRegistrar

import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;

import grails.util.GrailsConfig;
import java.util.Date;
import java.util.List;

public class CustomEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry reg) {
        reg.registerCustomEditor(Date.class, new CustomDateBinder(GrailsConfig.get("grails.date.formats", List.class)));
    }
}

and create a Spring-bean definition in your grails-app/conf/spring/resources.groovy:

beans = {
    "customEditorRegistrar"(CustomEditorRegistrar)
}

and finally define the date formats in your grails-app/conf/Config.groovy:

grails.date.formats = ["yyyy-MM-dd HH:mm:ss.SSS ZZZZ", "dd.MM.yyyy HH:mm:ss"]
Siegfried Puchbauer
Just wondering if there's a reason you would choose to implement this in Java (as above) rather than Groovy? The code would be quite a bit shorter with Groovy.
Don
I implemented a similar piece of code in Java in former times when Groovy was much slower than it is now. Groovy made a great leap forwand in this matter. I'm just reusing the old Java code out of lazyness ;-)
Siegfried Puchbauer
A: 

I implemented your solution and it works perfectly. However, it is not working in an integration test. When I run the application I can see the registrar is being created and then called, but not in the integration test, should I do something else to make it work in the integration test as well? or is there any way to directly call the registrar so the custom data binder works?

Thanks a lot!!

Maricel
A: 

I've tried this with 1.1.1 for use with XML REST services, but it seems to break basic forms when not using services... should this affect the basic functionality, with the multiple form fields/struct approach?

bollwyvl
A: 

Great code!

I think this may work better if you extend your CustomDateBinder from the original DateBinder in the Grails Code.

I would like to see this code as a Patch to grails, it would solve alot of problems. I cannot vote your answer up Siegfried Puchbauer but here is my +1.

Scott Warren
A: 

To use this with integration tests you need to be running version 1.2 of grails

Jonathan Chauncey