I'm trying to use a rest-service created in grails 2.5.5 which has a Date property as follows:
import grails.rest.*
#Resource(uri='/restDomain', readOnly=true)
class RestDomain {
Date myDate
}
But when calling from another grails app (v2.5.5 or v3.1.9) like this:
new RestBuilder().get('http://localhost:8080/restApp/restDomain.json').json.collect {
new RestDomain(it)
}
I get a Cannot cast object '2016-01-20T12:36:57Z' with class 'java.lang.String' to class 'java.util.Date' error.
I've already added this in application.yml (grails v3.1.9) at the very bottom of the file
---
grails:
databinding:
dateFormats:
- yyyy-MM-dd'T'HH:mm:ssX
- yyyy-MM-dd'T'HH:mm:ss.SSSX
and this in Config.groovy (grails v2.5.5)
grails.databinding.dateFormats = ["yyyy-MM-dd'T'HH:mm:ssX", "yyyy-MM-dd'T'HH:mm:ss.SSSX"]
but doesn't seem to work at all
Note: In both client apps I have the corresponding src/groovy/restApp/RestDomain.groovy class as follows:
package restApp
class RestDomain {
Date myDate
}
Note 2: The grails 3.1.9 client app has compile 'org.grails:grails-datastore-rest-client:6.0.0.M1' dependency the in build.gradle dependencies section, and the grails 2.5.5 client app has compile ':rest-client-builder:2.1.1' in the BuildConfig.groovy plugins section
Any help getting it to work in a Grails 2.5.x or Grails 3.1.x app would be really appreciated
The 'Z' at the end of your date string signifies that the time zone is UTC which is not a valid ISO 8601 time zone specification that you could parse with the 'X' in your time formats. It is a literal that should be parseable with
yyyy-MM-dd'T'HH:mm:ss'Z'
Adding an answer to provide a workaround on how I resolved this, but I will not mark it as the accepted answer because it is not the ideal way to solved it, so here it is:
Added this in my client controller (I guess that the bindData method is the key here):
new RestBuilder().get('http://localhost:8080/restApp/restDomain.json').json.collect {
def restDomainInstance = new RestDomain()
bindData(restDomainInstance, it)
restDomainInstance
}
and changed RestDomain.groovy to this
package restApp
import java.text.SimpleDateFormat
import org.grails.databinding.BindUsing
class RestDomain {
#BindUsing({ obj, source ->
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
.parse(source.getPropertyValue('myDate'))
})
Date myDate
}
The X in the SimpleDateFormat is for recognizing the timezone as UTC, so the jvm converts it to its current timezone later (in my case: -04:00 GMT)
Related
I'm writing a Quarkus extension (internal usage) and cannot figure how to correctly write the configuration part.
I need to setup a configuration that looks like this, a list of objects containing several configuration properties.
some:
list-of-config:
- prop1: value1
prop2: value2
prop3: value3
- prop1: value4
prop2: value5
prop3: value6
So i was thinking to use #ConfigGroup and a List<> as in the following example:
#ConfigRoot(name = "some", phase = ConfigPhase.BUILD_TIME)
public class MyConfig {
#ConfigItem(defaultValue = "true")
public boolean enabled;
public List<MyGroup> listOfConfig;
#ConfigGroup
public static class MyGroup {
#ConfigItem
public String prop1;
#ConfigItem
public String prop2;
#ConfigItem
public String prop3;
}
}
Unfortunately i get the following exception when starting the application:
SRCFG00013: No Converter registered for class org....MyConfig$MyGroup
#ConfigGroup works pretty well when they are loaded as a single object, but not as a List. I guess this is not really related to config group itself since i also have this error without using config group but simple objects.
Has anyone already tried to use a list of object as configuration for a Quarkus extension ? The official documentation lacks of example about that.
According to the documentation, any type not being listed or accepting a String parameter as constructor/valueOf/of cannot be used with List or Optional types, except Optional that can also be applied to a configuration group object. Unfortunately, Optional + config group is bugged as mentioned here
So looks like custom objects are not supported, which answer my question.
Quarkus is working in adding the use of config mapping for extensions as well, which should solve the issues and add the support for complex types in the future versions (currently 2.1.0 at the time of this question)
In my Grails app, the original date read from the database is equal to:
{ endDate=2015-10-19 19:00:00.0}
While the JSON result is:
{"endDate": "2015-10-19T16:00:00Z"}
I think this is maybe related to time zone conversion. How could I show the original date without any timezone conversions in JSON?
Depending on which time zone you're in, 2015-10-19 19:00:00.0 and 2015-10-19T16:00:00Z may not be different times, they may be just different representations of the same time (instant).
In my case, I use a custom marshaller to ensure that times in my API's JSON response always use the UTC time zone. My custom marshaller looks like this:
import org.springframework.stereotype.Component
#Component
class DateMarshaller implements CustomMarshaller {
#Override
def getSupportedTypes() {
Date
}
#Override
Closure getMarshaller() {
{ Date date ->
TimeZone tz = TimeZone.getTimeZone('UTC')
date?.format("yyyy-MM-dd'T'HH:mm:ss'Z'", tz)
}
}
}
Remember to register the package this marshaller is in for Spring bean scanning in Config.groovy. The interface it implements is:
interface CustomMarshaller {
/**
* Indicates the type(s) of object that this marshaller supports
* #return a {#link Class} or collection of {#link Class}
* if the marshaller supports multiple types
*/
def getSupportedTypes()
Closure getMarshaller()
}
Then I have a service that registers all my instances of CustomMarshaller for the relevant type(s):
import grails.converters.JSON
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import javax.annotation.PostConstruct
class MarshallerRegistrarService implements ApplicationContextAware {
static transactional = false
ApplicationContext applicationContext
// a combination of eager bean initialization and #PostConstruct ensures that the marshallers are registered when
// the app (or a test thereof) starts
boolean lazyInit = false
#PostConstruct
void registerMarshallers() {
Map<String, CustomMarshaller> marshallerBeans = applicationContext.getBeansOfType(CustomMarshaller)
marshallerBeans.values().each { CustomMarshaller customMarshaller ->
customMarshaller.supportedTypes.each { Class supportedType ->
JSON.registerObjectMarshaller supportedType, customMarshaller.marshaller
}
}
}
}
This is a fairly involved solution, but In my case I'm using Grails 2.5.X. If I was using Grails 3.X I'd try to use JSON views instead.
If my memory is correct, JSON spec does not actually define a format for date format. But everybody uses ISO 8601, so it sort of de-facto standard. And most just always use Zulu time zone.
I searched myself some time ago, on ways to force Grails JSON to render dates in certain time zone, but failed. In my Grails web app, I just declare date fields as text and format them to proper time zone and format in my own code. On the good side, it also has an added benefit that you can guarantee it to stay that way in future. (I'm, using Grails since about 1.1 and did see breaking changes on several occasions.)
Working on an old application that is built with grails 2.3.11 in which for some unbeknownst reason #Dónal's solution wasn't working. Fortunately, I found an easier/more straightforward approach to register a custom marshaller on mrhaki's blog.
So, I made a copy of the default DateMarshaller that resides in org.codehaus.groovy.grails.web.converters.marshaller.json and modified the date formatter in my custom DateMarshaller and then registered the marshaller using the snippet below, taken from mrhaki's post.
dateMarshaller(ObjectMarshallerRegisterer) {
// Assign custom marshaller instance.
marshaller = new DateMarshaller()
// Set converter class type.
converterClass = JSON
// Optional set priority. Default value is 0.
priority = 1
}
Just make sure that your Custom Marshaller has a higher priority than the default one. Works like a charm!
I have this Data Object with an Int64 column:
[TableAttribute(Name="dbo.vw_RelationLineOfBusiness")]
[DataServiceKey("ProviderRelationLobId")]
public partial class RelationLineOfBusiness
{
#region Column Mappings
private System.Guid _Lineofbusiness;
private System.String _ContractNumber;
private System.Nullable<System.Int32> _ProviderType;
private System.String _InsuredProviderType;
private System.Guid _ProviderRelationLobId;
private System.String _LineOfBusinessDesc;
private System.String _CultureCode;
private System.String _ContractDesc;
private System.Nullable<System.Guid> _ProviderRelationKey;
private System.String _ProviderRelationNbr;
**private System.Int64 _AssignedNbr;**
When I post/Put object through my OData controller using HttpClient and NewtsonSoft:
partial class RelationLineOfBusinessController : ODataController
{
public HttpResponseMessage PutRelationLineOfBusiness([FromODataUri] System.Guid key, Invidasys.VidaPro.Model.RelationLineOfBusiness entity)
the entity object is null and the error in my modelstate :
"Cannot convert a primitive value to the expected type 'Edm.Int64'. See the inner exception for more details."
I noticed when I do a get on my object using the below URL:
Invidasys.Rest.Service/VidaPro/RelationLineOfBusiness(guid'c6824edc-23b4-4f76-a777-108d482c0fee')
my json looks like the following - I noticed that the AssignedNbr is treated as a string.
{
"odata.metadata":"Invidasys.Rest.Service/VIDAPro/$metadata#RelationLineOfBusiness/#Element",
"Lineofbusiness":"ba129c95-c5bb-4e40-993e-c28ca86fffe4","ContractNumber":null,"ProviderType":null,
"InsuredProviderType":"PCP","ProviderRelationLobId":"c6824edc-23b4-4f76-a777-108d482c0fee",
"LineOfBusinessDesc":"MEDICAID","CultureCode":"en-US","ContractDesc":null,
"ProviderRelationKey":"a2d3b61f-3d76-46f4-9887-f2b0c8966914","ProviderRelationNbr":"4565454645",
"AssignedNbr":"1000000045","Ispar":true,"ProviderTypeDesc":null,"InsuredProviderTypeDesc":"Primary Care Physician",
"StartDate":"2012-01-01T00:00:00","EndDate":"2014-01-01T00:00:00","Created":"2014-06-13T10:59:33.567",
"CreatedBy":"Michael","Updated":"2014-06-13T10:59:33.567","UpdatedBy":"Michael"
}
When I do a PUT with httpclient the JSON is showing up in my restful services as the following and the json for the AssignedNbr column is not in quotes which results in the restful services failing to build the JSON back to an object. I played with the JSON and put the AssignedNbr in quotes and the request goes through correctly.
{"AssignedNbr":1000000045,"ContractDesc":null,"ContractNumber":null,"Created":"/Date(1402682373567-0700)/",
"CreatedBy":"Michael","CultureCode":"en-US","EndDate":"/Date(1388559600000-0700)/","InsuredProviderType":"PCP",
"InsuredProviderTypeDesc":"Primary Care Physician","Ispar":true,"LineOfBusinessDesc":"MEDICAID",
"Lineofbusiness":"ba129c95-c5bb-4e40-993e-c28ca86fffe4","ProviderRelationKey":"a2d3b61f-3d76-46f4-9887-f2b0c8966914",
"ProviderRelationLobId":"c6824edc-23b4-4f76-a777-108d482c0fee","ProviderRelationNbr":"4565454645","ProviderType":null,
"ProviderTypeDesc":null,"StartDate":"/Date(1325401200000-0700)/","Updated":"/Date(1408374995760-0700)/","UpdatedBy":"ED"}
The reason we wanted to expose our business model as restful services was to hide any data validation and expose all our databases in format that is easy to develop against. I looked at the DataServiceContext to see if it would work and it does but it uses XML to communicate between the restful services and the client. Which would work but DataServiceContext does not give the level of messaging that HttpRequestMessage/HttpResponseMessage gives me for informing users on the errors/missing information with their post.
We are planning on supporting multiple devices from our restful services platform but that requires that I can use NewtonSoft Json as well as Microsoft's DataContractJsonSerializer if need be.
My question is for a restful service standpoint - is there a way I can configure/code the restful services to take in the AssignedNbr as in JSON as without the quotes.
Or from a JSON standpoint is their a way I can get the JSON built without getting into the serializing business nor do I want our clients to have deal with custom serializers if they want to write their own apps against our restful services.
Any suggestions?
Thanks.
I think you can migrate to Web API 2.2 for OData V4. Here's the information:
Announcing the Release of ASP.NET MVC 5.2, Web API 2.2 and Web Pages 3.2
OData V4 Spec says:
3.2 Controlling the Representation of Numbers
The IEEE754Compatible=true format parameter indicates that the service MUST serialize Edm.Int64 and Edm.Decimal numbers (including the odata.count, if requested) as strings. If not specified, or specified as IEEE754Compatible=false, all numbers MUST be serialized as JSON numbers.
This enables support for JavaScript numbers that are defined to be 64-bit binary format IEEE 754 values [ECMAScript] (see section 4.3.1.9) resulting in integers losing precision past 15 digits, and decimals losing precision due to the conversion from base 10 to base 2.
OData JSON payloads that format Edm.Int64 and Edm.Decimal values as strings MUST specify this format parameter in the media type returned in the Content-Type header.
So, for payload as:
#"{
""Lineofbusiness"": ""ba129c95-c5bb-4e40-993e-c28ca86fffe4"",
""AssignedNbr"": ""1000000045""
}";
you should set:
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;IEEE754Compatible=true");
Otherwise, you shouldn't.
Sam Xu is exactly right and should be marked as the answer.
However, I wanted to add exactly what you need to do to add this to the pipeline.
First, you can set this global, per route etc. You can find that information here:
http://www.asp.net/web-api/overview/advanced/http-message-handlers
Below you'll find an example that will work.
public static void Configuration(IAppBuilder builder)
{
HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.Add(new MethodOverrideHandler());
}
public class MethodOverrideHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;IEEE754Compatible=true");
return base.SendAsync(request, cancellationToken);
}
}
Alternatively, try changing the type you send to your web api to Number instead of string
Also, check the Type of the decimal that you are sending. If it's type 'string' you can change it to type number. For my service, making this change no longer throws the error.
//Gnuget Package Manager Install-Package numeral
if (typeof newValue === 'string')
{
newValue = numeral().unformat(newValue);
}
odatajs.oData.request(
{
requestUri: xxx,
method: "PATCH",
data: { PriceNull: newValue }
}
I have a MVC3 project that manages events' dates. The problem is that when I write a JsonResult
//...
var StartDate=new DateTime(1999,12,10,2,3,40);
return Json(StartDate,JsonBehavior.AllowGet);
the response's body format is something similar to
"StartDate":"\/Date(1374962232247)\/"
This result is giving me a date in the last day according to the server timezone, Instead of returning something like
"StartDate":"\/Date(1374962232247-0600)\/"
which contains some server timezone info!
I can't follow the SHanselman's post(like it but It seems not appropriated)!
So, can someone tell me how to let Newtonsoft.Json.5.0.6 packages convert Json DateTime with Timezone info?
Kind Regards
I think this link should help you with setting up MVC3 project working with Newtonsoft Json Serializer. For ISO date time formatter you should change the following lines in the JsonNetResult constructor.
public JsonNetResult()
{
Formatting = Formatting.None;
var settings = new JsonSerializerSettings();
settings.Converters.Add(new IsoDateTimeConverter());
SerializerSettings = settings;
JsonRequestBehavior = JsonRequestBehavior.DenyGet;
}
You have to derive your controllers from BaseController to use the custom result.
hope this helps.
I am new to Grails and am using Grails 2.1 with a MySQL 5.5 backend to build a sample project to learn.
I installed JodaTime 1.4 Plug-in and then ran grails install-joda-time-templates
However, when I declared a Domain Class field to be of type org.joda.time.DateTime, I got an error when attempting to save a new entry.
In order to isolate the problem, I created a simple Domain Class:
import org.joda.time.DateTime
class Project
{
String name
DateTime startDate
static constraints = {
name(blank: false, maxSize: 50)
startDate(validator: {return (it > new DateTime())})
}
}
The controller just sets scaffold to use the Domain Class.
My DataSource.groovy specifies dbCreate = "create-drop", as I am letting the tables get created by Grails.
Here is the error I get when I try to save:
Class:com.mysql.jdbc.MysqlDataTruncation
Message:Data truncation: Data too long for column 'start_date' at row 1
When I look at project.start_date column in the MySQL database that Grails created, the type is TINYBLOB.
My thought is that TINYBLOB may not be sufficient to store the data for a JodaTime DateTime field.
Does anyone know how I can make Grails create an appropriate type?
Thank you very much.
In your Config.groovy:
grails.gorm.default.mapping = {
"user-type" type: PersistentDateTime, class: DateTime
"user-type" type: PersistentLocalDate, class: LocalDate
}
And your mapping closure:
static mapping = {
startDate type: PersistentDateTime
}
Take a look at this post for more info, see if it helps.
What I did to make it work (Grails 2.1):
1) add to buildConfig:
compile "org.jadira.usertype:usertype.jodatime:1.9"
2) refresh the dependencies
3) run this command to add the user-type supported:
grails install-joda-time-gorm-mappings
4) finally in the domain class:
import org.jadira.usertype.dateandtime.joda.*
static mapping = {
column_name type: PersistentDateTime
}
Documentation was found here: persistence