smartgwt restdatasource json日期验证

时间:2013-05-09 15:17:04

标签: json rest date smartgwt

我使用Spring 3.2 MVC Controller和Spring-WS来创建RESTful Web服务。 Spring控制器接受一个对象文件正确更新数据库,然后将JSON返回给前端。为JSON的消息转换设置了Spring Context。我有这些单元测试,所以我知道Spring控制器正在工作并且相应地提交数据。

当我从网络服务中获取数据/ JSON时出现错误,实际上是警告:

10:05:08.906[ERROR[Phonebook]10:05:08.902:XRP3:WARN:RestDataSource:restUserDS:restUserDS.userBirthDate:value:-99187200000 failed on validator  {type:"isDate",typeCastValidator:true,_generated:true,defaultErrorMessage:"Must be a date."}

com.smartgwt.client.core.JsObject$SGWT_WARN:    10:05:08.902:XRP3:WARN:RestDataSource:restUserDS:restUserDS.userBirthDate: value: -99187200000 failed on validator: {type: "isDate",typeCastValidator: true,_generated: true,defaultErrorMessage: "Must be a date."}
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:172)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:293)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:547)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
at java.lang.Thread.run(Thread.java:662)

所以,这是我的UserDataSource:

package com.opensource.restful.client.datasource;

import java.util.HashMap;
import java.util.Map;
import com.google.gwt.core.client.JavaScriptObject;
import com.opensource.restful.shared.Constants;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.OperationBinding;
import com.smartgwt.client.data.RestDataSource;
import com.smartgwt.client.data.fields.DataSourceBooleanField;
import com.smartgwt.client.data.fields.DataSourceDateField;
import com.smartgwt.client.data.fields.DataSourceIntegerField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.types.DSDataFormat;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.DSProtocol;
import com.smartgwt.client.util.JSOHelper;
import com.smartgwt.client.util.JSON;

public class UserDataSource extends RestDataSource
{
private static UserDataSource instance = null;

public static UserDataSource getInstance()
{
    if (instance == null)
    {
        instance = new UserDataSource("restUserDS");
    }

    return instance;
}

private UserDataSource(String id)
{
    setID(id);
    setClientOnly(false);

    // set up FETCH to use GET requests
    OperationBinding fetch = new OperationBinding();
    fetch.setOperationType(DSOperationType.FETCH);
    fetch.setDataProtocol(DSProtocol.GETPARAMS);
    DSRequest fetchProps = new DSRequest();
    fetchProps.setHttpMethod("GET");
    fetch.setRequestProperties(fetchProps);

    // set up ADD to use POST requests
    OperationBinding add = new OperationBinding();
    add.setOperationType(DSOperationType.ADD);
    add.setDataProtocol(DSProtocol.POSTMESSAGE);
    // ===========================================
    DSRequest addProps = new DSRequest();
    addProps.setHttpMethod("POST");
    // addProps.setContentType("application/json");
    add.setRequestProperties(addProps);

    // set up UPDATE to use PUT
    OperationBinding update = new OperationBinding();
    update.setOperationType(DSOperationType.UPDATE);
    update.setDataProtocol(DSProtocol.POSTMESSAGE);
    // ===========================================
    DSRequest updateProps = new DSRequest();
    updateProps.setHttpMethod("PUT");
    // updateProps.setContentType("application/json");
    update.setRequestProperties(updateProps);

    // set up REMOVE to use DELETE
    OperationBinding remove = new OperationBinding();
    remove.setOperationType(DSOperationType.REMOVE);
    DSRequest removeProps = new DSRequest();
    removeProps.setHttpMethod("DELETE");
    remove.setRequestProperties(removeProps);

    // apply all the operational bindings
    setOperationBindings(fetch, add, update, remove);

    init();
}

private DataSourceIntegerField userIdField;
private DataSourceBooleanField userActiveField;
private DataSourceTextField usernameField;
private DataSourceTextField passwordField;
private DataSourceTextField firstnameField;
private DataSourceTextField lastnameField;
private DataSourceTextField emailField;
private DataSourceTextField securityQuestion1Field;
private DataSourceTextField securityAnswer1Field;
private DataSourceTextField securityQuestion2Field;
private DataSourceTextField securityAnswer2Field;
private DataSourceDateField birthdateField;

private DataSourceIntegerField positionIdField;

protected void init()
{
    setDataFormat(DSDataFormat.JSON);
    setJsonRecordXPath("/");

    // set the values for the datasource
    userIdField = new DataSourceIntegerField(Constants.USER_ID, Constants.TITLE_USER_ID);
    userIdField.setPrimaryKey(true);
    userIdField.setCanEdit(false);

    userActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);

    usernameField = new DataSourceTextField(Constants.USER_USERNAME, Constants.TITLE_USER_USERNAME);
    passwordField = new DataSourceTextField(Constants.USER_PASSWORD, Constants.TITLE_USER_PASSWORD);

    firstnameField = new DataSourceTextField(Constants.USER_FIRST_NAME, Constants.TITLE_USER_FIRST_NAME);
    lastnameField = new DataSourceTextField(Constants.USER_LAST_NAME, Constants.TITLE_USER_LAST_NAME);

    emailField = new DataSourceTextField(Constants.USER_EMAIL, Constants.TITLE_USER_EMAIL);

    securityQuestion1Field =
        new DataSourceTextField(Constants.USER_SECURITY_QUESTION_1, Constants.TITLE_USER_SECURITY_QUESTION_1);
    securityAnswer1Field =
        new DataSourceTextField(Constants.USER_SECURITY_ANSWER_1, Constants.TITLE_USER_SECURITY_ANSWER_1);
    securityQuestion2Field =
        new DataSourceTextField(Constants.USER_SECURITY_QUESTION_2, Constants.TITLE_USER_SECURITY_QUESTION_2);
    securityAnswer2Field =
        new DataSourceTextField(Constants.USER_SECURITY_ANSWER_2, Constants.TITLE_USER_SECURITY_ANSWER_2);

    birthdateField = new DataSourceDateField(Constants.USER_BIRTHDATE, Constants.TITLE_USER_BIRTHDATE);

    positionIdField = new DataSourceIntegerField(Constants.USER_POSITION_ID, Constants.TITLE_USER_POSITION_ID);
    // positionActiveField = new DataSourceBooleanField(Constants.USER_ACTIVE, Constants.TITLE_USER_ACTIVE);
    // positionCodeField;
    // positionDescriptionField;

    setFields(userIdField, userActiveField, usernameField, passwordField, firstnameField, lastnameField,
        emailField, birthdateField, securityQuestion1Field, securityAnswer1Field, securityQuestion2Field,
        securityAnswer2Field, positionIdField);

    setFetchDataURL(getServiceRoot() + "/userId/{id}"); // works great
    setAddDataURL(getServiceRoot() + "/create");
    setUpdateDataURL(getServiceRoot() + "/update");
    setRemoveDataURL(getServiceRoot() + "/remove"); // works great
}

protected String getServiceRoot()
{
    return "rest/users";
}

protected String getPrimaryKeyProperty()
{
    return "userId";
}

@Override
protected Object transformRequest(DSRequest dsRequest)
{
    System.out.println("UserDataSource: transformRequest: START");
    dsRequest.setContentType("application/json");
    JavaScriptObject jso = dsRequest.getData();

    String jsoText = JSON.encode(jso);

    System.out.println("UserDataSource: transformRequest: START: jsoText=" + jsoText);
    // ================================================================================
    // String strDob = JSOHelper.getAttribute(jso, Constants.USER_BIRTHDATE);
    // Date dateDob = JSOHelper.getAttributeAsDate(jso, Constants.USER_BIRTHDATE);
    // JSOHelper.setAttribute(jso, Constants.USER_BIRTHDATE, dateDob.getTime());
    // System.out.println("UserDataSource: transformRequest: START2: jsoText2=" + jsoText);
    // ================================================================================

    // get the user position id which comes from the UI
    // the name of this field from the UI 'userPositionId'
    String userPositionId = JSOHelper.getAttribute(jso, Constants.USER_POSITION_ID);

    // create a small JavaScriptObject to be used for the position
    // the JSON string would look like {"id":x} x = userPositionId
    Map mapPositionId = new HashMap();
    mapPositionId.put("id", userPositionId);
    JavaScriptObject jsoPositionId = JSOHelper.convertMapToJavascriptObject(mapPositionId);

    // This creates the new JSON attribute:
    // ... , "position":{"id":x}
    JSOHelper.setAttribute(jso, "position", jsoPositionId);

    // remove the JSON Attribute: ... , "userPositionId":x
    JSOHelper.deleteAttribute(jso, Constants.USER_POSITION_ID);

    String s1 = JSON.encode(jso);
    System.out.println("UserDataSource: transformRequest: FINISH: s1=" + s1);
    return s1;
    // return super.transformRequest(dsRequest);
}

protected void transformResponse(DSResponse response, DSRequest request, Object data)
{
    System.out.println("UserDataSource: transformResponse: START");
    super.transformResponse(response, request, data);
    System.out.println("UserDataSource: transformResponse: FINISH");
}

}

我可以确认我发送数据/ JSON就好了。我必须稍微改一下才能添加我要发回的属性。而且我相信这是TransformRequest的目的。 接收Update的Spring MVC Controller看起来像:

@RequestMapping(value="/update",
method=RequestMethod.PUT,produces="application/json",
headers="content-type=application/json")
public @ResponseBody UserDTO updateUser(@RequestBody UserDTO user)
{
    System.out.println("UserController: START: updateUser: user=" + user);
    UserEntity userEntity = service.update(user);
    UserDTO userDto = Mapping.mappingUser(userEntity);
    System.out.println("UserController: FINISH: updateUser: userDto=" + userDto);
    return userDto;
}

我可以确认我收到了有效的UserDTO。当我查看transformResponse时:

System.out.println("UserDataSource: transformResponse: START");
super.transformResponse(response, request, data);
System.out.println("UserDataSource: transformResponse: FINISH");

我在第一个println上得到了错误,我还没有完成super.transformResponse。当我查看回来的数据时,这是我要回来的JSON。

{
"userId":1, 
"userActive":true, 
"position":{
    "id":1, 
    "active":true, 
    "code":"ADMIN", 
    "description":"Administrator"
}, 
"username":"demo", 
"password":"demo", 
"otherPassword":null, 
"userFirstName":"DemoXXX", 
"userLastName":"DemoXXX", 
"userEmail":"tom@tomholmes.netXXX", 
"userSecurityQuestion1":"Meaning of Life?XXX", 
"userSecurityAnswer1":"42XX", 
"userSecurityQuestion2":"aaaXX", 
"userSecurityAnswer2":"bbbXX", 
"userBirthDate":-99100800000, 
"contacts":[
    {
        "contactId":2, 
        "userId":1, 
        "prefix":"Mr.", 
        "firstName":"updated_fn", 
        "middleName":null, 
        "lastName":"updated_ln", 
        "suffix":"Jr.", 
        "address1":"123 main street", 
        "address2":"Apt. 456", 
        "city":"Randolph", 
        "state":"MA", 
        "zip":"12345-1234", 
        "companyId":0, 
        "enteredBy":0, 
        "enteredDate":null, 
        "editedBy":0, 
        "editedDate":null, 
        "birthDate":null, 
        "emails":null, 
        "phones":null, 
        "links":null
    }
], 
"userPositionId":null
}

那么......如何修复我的数据源或transformResponse以删除此警告? JSON似乎是正确的,唯一的问题是“userBirthDate”当它作为一个长的负数回来时,我假设从纪元开始的毫秒数。我可以在JSON / Jackson Mapper中进行一些更改以更改日期的格式吗?

感谢您的帮助!

更新1: 下面提供的帮助很有帮助,现在我知道这不是SmartGWT或RestDataSource问题,而是严格意义上jackson如何在对象中转换java.util.Date。转换将日期更改为负长号,并且应具有其他格式。我正在使用Spring 3.2并使用旧的Jackson 1.9.14。但现在,我升级到Jackson 2,我的pom.xml现在使用:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
     <version>2.1.4</version>
</dependency>

在我的spring-servlext.xml中:

   <context:component-scan base-package="com.opensource.restful" />

<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">

    <property name="supportedMediaTypes" value="application/json"/>

       <property name="objectMapper">
             <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                 <property name="dateFormat">
                 <bean class="java.text.SimpleDateFormat">
                 <constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ssZ"></constructor-arg>
                 </bean>
                 </property>
             </bean>
          </property>
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="messageConverters">
      <list>
        <ref bean="jsonHttpMessageConverter"/>
      </list>
  </property>
</bean>

<mvc:annotation-driven />    

我已经谷歌搜索了几个小时,并寻找在Spring配置中使用Jackson2映射器的解决方案,并且在我确保所有bean定义都正确之后,userBirthDate仍然以负数长度返回。我确信这个配置可以稍微调整一下,以便按照我想要的方式调整,所以日期以ISO格式返回:yyyy-MM-dd'T'HH:mm:ssZ

感谢您帮助我靠近。

更新2: 我想我做到了。如前所述,我升级到Jackson2,据我所知,它已经是Spring 3.2的一部分,这是我正在使用的Spring版本。

我正在使用的spring-servlet.xml,它的工作方式如下:

<context:component-scan base-package="com.opensource.restful" />

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
          <property name="objectMapper">
             <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                 <property name="dateFormat">
                 <bean class="java.text.SimpleDateFormat">
                 <constructor-arg type="java.lang.String" value="yyyy-MM-dd'T'HH:mm:ssZ"></constructor-arg>
                 </bean>
                 </property>
             </bean>
          </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <property name="supportedMediaTypes" value="application/json"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="messageConverters">
      <list>
        <ref bean="jsonHttpMessageConverter" />
      </list>
  </property>
</bean>


<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
    <property name="messageConverters">
        <list>
        <ref bean="jsonHttpMessageConverter" />
        </list>
    </property>
</bean>

我不得不第二次添加MappingJackson2HttpMessageConverter,因为它在restTemplate中被引用...但是如果我可以只定义一次,那就没问题了。所以,也许有人可以帮我更好地定义spring-servlet.xml。

无论如何,此更改有效,因此JSON日期将返回:

 "userBirthDate":"1966-11-03T00:00:00-0500"   

所以,到目前为止这是进展。

1 个答案:

答案 0 :(得分:1)

从验证错误 - defaultErrorMessage:“必须是日期”

由于birthdateField是DataSourceDateField,因此UserDTO.userBirthDate必须是java.util.Date或类似且Date getUserBirthDate()Constants.USER_BIRTHDATE必须设置为"userBirthDate"

如果上述所有内容都正常,则由于java.util.Date对象的默认序列化为JSON。
有关其他信息,请查看以下内容 http://java.dzone.com/articles/how-serialize-javautildate(不要使用静态SimpleDateFormat)
Spring 3.1 JSON date format
jackson2 JSON ISO 8601 date from JodaTime in Spring 3.2RC1

使用以下日期格式时,SmartGWT效果最佳(例如,2013-05-09T00:00:00)。
yyyy-MM-dd'T'HH:mm:ss

System.out.println()无法在SmartGWT / GWT中使用,因为客户端代码转换为JavaScript并在浏览器内运行,没有JVM。

在这种情况下,您可能不需要使用transformResponse()