Convert Unix timestamp to Java Date, Spring RequestParam - spring-mvc

Following is a request fullcalendar js send to the server.
http://localhost:8080/NVB/rest/calendar/events?start=1425168000&end=1428796800 400
How to specify Date pattern (#DateTimeFormat) in Spring Request Param to convert this time to a Date object. I tried different patterns but getting 405 Bad Request.
#RequestMapping(value = "/events", method = RequestMethod.GET)
public #ResponseBody List<EventDto> addOrder(#RequestParam(value = "start") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date start,
#RequestParam(value = "end") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date end) {
LOGGER.info("Requesting event from [{}] to [{}]", start, end);
return new LinkedList<EventDto>();
}

Since timestamps aren't a formatted date (going by Java's SimpleDateFormat options), but more a numeric value: I would recommend making a custom data-binder for Date objects if you're doing this more often than this single instance. See http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#portlet-ann-webdatabinder
As a one-off solution you can bind them to Long parameters and create your own Date object with new Date(start).

Using #InitBinder and WebDataBinder:
#RestController
public class SimpleController {
//... your handlers here...
#InitBinder
public void initBinder(final WebDataBinder webdataBinder) {
webdataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Date(Long.valueOf(text)));
}
});
}
}

Related

Input date to webapi

I am using webapi2. I have a property in model is start date whose datatype is datetime. I want to pass the date as "dd-mm-yyyy" format. But if i send, i am getting 400 bad request. Could you please help me out. Note, I am using Fluent validation for the model validation.
public class Item
{
public DateTime? StartDate { get; set; }
public string Id { get; set; }
}
I want to pass the date as "dd-mm-yyyy"`
You have 3 options.
Option ISO8601
Don't pass it as "dd-mm-yyyy". Pass it instead in ISO8601 format (yyyy-MM-dd). That is the correct way to serialize DateTimes to string and also for then communicating that string representation between tiers. This format is a standard, widely used, unambiguous, and almost all frameworks that I am aware of have built in mechanisms for outputting DateTimes to that format and parsing them from that format.
Displaying a DateTime formatted as "dd-mm-yyyy" is a presentation layer concern and it should stay there and not "bleed" into the other application layers.
Option Formatters
Use custom code, like a Json Converte or an ActionFilterAttribute, to read the incoming DateTime.
Option String
Accept a string parameter instead and handle your own parsing inside the controller's method.
I honestly do not recommend the last 2 options. Instead use ISO8601: a standard, unambiguous, widely accepted means of communicating a DateTime.
I have created a custom value provider factory and am using the default model binding.
public class OrderValueProviderFactory<T> : ValueProviderFactory where T : class
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
var querystring = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key.ToLower(), x => x.Value);
return new OrderValueProvider<T>(querystring);
}
}
public class OrderValueProvider<T> : IValueProvider
{
readonly Dictionary<string, string> querystring;
public OrderValueProvider(Dictionary<string, string> _querystring)
{
querystring = _querystring;
}
public bool ContainsPrefix(string prefix)
{
return true;
}
public ValueProviderResult GetValue(string key)
{
T obj = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(string))
{
property.SetValue(obj, querystring.GetStringValue(property.Name.ToLower()));
}
else if (property.PropertyType == typeof(DateTime?))
{
property.SetValue(obj, querystring.GetDateTimeValue(property.Name.ToLower()));
}
else if (property.PropertyType == typeof(int))
{
property.SetValue(obj, querystring.GetIntValue(property.Name.ToLower()));
}
}
return new ValueProviderResult(obj, "", CultureInfo.InvariantCulture);
}
}

Convert asp.net / MS proprietary json Dateformat to java8 LocalDateTime with jackson while deserializing json to object

I call a webservice from a Spring Boot App, using jackson-jsr-310 as maven dependency for being able to make use of LocalDateTime:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders httpHeaders = this.createHeaders();
ResponseEntity<String> response;
response = restTemplate.exchange(uri,HttpMethod.GET,new HttpEntity<Object>(httpHeaders),String.class);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
BusinessPartner test = mapper.readValue(response.getBody(), BusinessPartner.class);
My problem is in the last line, the code produces this error:
java.time.format.DateTimeParseException: Text '/Date(591321600000)/' could not be parsed at index 0
The resulting JSON in response.getBody() looks like this:
{
"d":{
...
"Address":{...},
"FirstName":"asd",
"LastName":"asd",
"BirthDate":"\/Date(591321600000)\/",
}
}
And in my model class, I have the following member:
#JsonProperty("BirthDate")
private LocalDateTime birthDate;
So, after a bit of searching here I found out that this /Date(...)/ seems to be a Microsoft-proprietary Dateformat, which Jackson cannot deserialize into an object per default.
Some questions advise to create a custom SimpleDateFormat and apply it to the opbject mapper, which I tried to do, but then I think I miss the right syntax for mapper.setDateFormat(new SimpleDateFormat("..."));
I tried with e.g. mapper.setDateFormat(new SimpleDateFormat("/Date(S)/"));
or at the end even mapper.setDateFormat(new SimpleDateFormat("SSSSSSSSSSSS)"));
but it seems this does not work, too, so I am out of ideas for now and hope some people here could help me out.
edit 1:
further investigated, it seems one way to go is to write a custom DateDeSerializer for jackson. So I tried this:
#Component
public class JsonDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
private DateTimeFormatter formatter;
private JsonDateTimeDeserializer() {
this(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
public JsonDateTimeDeserializer(DateTimeFormatter formatter) {
this.formatter = formatter;
}
#Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException
{
if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
String unixEpochString = parser.getText().trim();
unixEpochString = unixEpochString.replaceAll("[^\\d.]", "");
long unixTime = Long.valueOf(unixEpochString);
if (unixEpochString.length() == 0) {
return null;
}
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(unixTime), ZoneId.systemDefault());
localDateTime.format(formatter);
return localDateTime;
}
return null;
}
}
which actually returns nearly what I want, annotating my fields in the model using
#JsonDeserialize(using = JsonDateTimeDeserializer.class)
but not exactly:
This code returns a LocalDateTime of value: 1988-09-27T01:00.
But in the thirdparty system, the xmlvalue is 1988-09-27T00:00:00.
As it is obvious, the ZoneId here:
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(unixTime), ZoneId.systemDefault());
is the Problem, apart from a wrong dateformat.
So could someone here please help me out in how to switch to always use zeros for the time-part and to get my dateformat right? Would be great!
I'm assuming that the number 591321600000 is the epoch milli (number of milliseconds from 1970-01-01T00:00:00Z).
If that's the case, I think that SimpleDateFormat can't help you (at least I couldn't find a way to parse a date from the epoch milli using this class). The pattern S (according to javadoc) is used to format or parse the milliseconds field of a time (so its maximum value is 999) and won't work for your case.
The only way I could make it work is creating a custom deserializer.
First, I created this class:
public class SimpleDateTest {
#JsonProperty("BirthDate")
private LocalDateTime birthDate;
// getter and setter
}
Then I created the custom deserializer and added it to a custom module:
// I'll explain all the details below
public class CustomDateDeserializer extends JsonDeserializer<LocalDateTime> {
#Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String s = p.getText(); // s is "/Date(591321600000)/"
// assuming the format is always /Date(number)/
long millis = Long.parseLong(s.replaceAll("\\/Date\\((\\d+)\\)\\/", "$1"));
Instant instant = Instant.ofEpochMilli(millis); // 1988-09-27T00:00:00Z
// instant is in UTC (no timezone assigned to it)
// to get the local datetime, you must provide a timezone
// I'm just using system's default, but you must use whatever timezone your system uses
return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
public class CustomDateModule extends SimpleModule {
public CustomDateModule() {
addDeserializer(LocalDateTime.class, new CustomDateDeserializer());
}
}
Then I added this module to my mapper and it worked:
// using reduced JSON with only the relevant field
String json = "{ \"BirthDate\": \"\\/Date(591321600000)\\/\" }";
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
// add my custom module
mapper.registerModule(new CustomDateModule());
SimpleDateTest value = mapper.readValue(json, SimpleDateTest.class);
System.out.println(value.getBirthDate()); // 1988-09-26T21:00
Now some comments about the deserializer method.
First I converted the millis 591321600000 to an Instant (a class that represents a UTC instant). 591321600000 in millis is equivalent to 1988-09-27T00:00:00Z.
But that's the UTC date/time. To get the local date and time, you must know in what timezone you are, because in every timezone it's a different date and time (everybody in the world are at the same instant, but their local date/time might be different, depending on where they are).
In my example, I just used ZoneId.systemDefault(), which gets the default timezone of my system. But if you don't want to depend on the default and want to use a specific timezone, use the ZoneId.of("timezone name") method (you can get the list of all available timezones names with ZoneId.getAvailableZoneIds() - this method returns all valid names accepted by the ZoneId.of() method).
As my default timezone is America/Sao_Paulo, this code sets the birthDate to 1988-09-26T21:00.
If you don't want to convert to a specific timezone, you can use the ZoneOffset.UTC. So, in the deserializer method, the last line will be:
return instant.atZone(ZoneOffset.UTC).toLocalDateTime();
Now the local date will be 1988-09-27T00:00 - as we're using UTC offset, there's no timezone conversion and the local date/time is not changed.
PS: if you need to convert the birthDate back to MS's custom format, you can write a custom serializer and add to the custom module as well. To convert a LocalDateTime to that format, you can do:
LocalDateTime birthDate = value.getBirthDate();
// you must know in what zone you are to convert it to epoch milli (using default as an example)
Instant instant = birthDate.atZone(ZoneId.systemDefault()).toInstant();
String msFormat = "/Date(" + instant.toEpochMilli() + ")/";
System.out.println(msFormat); // /Date(591321600000)/
Note that, to convert a LocalDateTime to Instant, you must know in what timezone you are. In this case, I recommend to use the same timezone for serializing and deserializing (in your case, you can use ZoneOffset.UTC instead of ZoneId.systemDefault().
Here's some Groovy code I wrote that also handles the timezone offset: https://gist.github.com/jeffsheets/938733963c03208afd74927fb6130884
class JsonDotNetLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
#Override
LocalDateTime deserialize(JsonParser parser, DeserializationContext ctxt) {
convertDotNetDateToJava(parser.text.trim())
}
/**
* Returns a Java LocalDateTime when given a .Net Date String
* /Date(1535491858840-0500)/
*/
static LocalDateTime convertDotNetDateToJava(String dotNetDate) {
// Strip the prefix and suffix to just 1535491858840-0500
String epochAndOffset = dotNetDate[6..-3]
// 1535491858840
String epoch = epochAndOffset[0..-6]
// -0500 Note, keep the negative/positive indicator
String offset = epochAndOffset[-5..-1]
ZoneId zoneId = ZoneId.of("UTC${offset}")
LocalDateTime.ofInstant(Instant.ofEpochMilli(epoch.toLong()), zoneId)
}
}

Spring Data, MVC DomainClassConverter how to get to id from request and keep using domain type as param

As part of Spring Data, there is DomainClassConverter which helps with repository lookups so that we don't have to do lookup manually.
http://docs.spring.io/spring-data/commons/docs/current/reference/html/#core.web.basic.domain-class-converter
#Controller
#RequestMapping("/orders/{id}")
public class PaymentController {
#RequestMapping(path = "/payment", method = PUT)
ResponseEntity<?> submitPayment(#PathVariable("id") Order order) {
if (order == null) {
throw new OrderNotFoundException(???orderId???);
}
...
}
}
How can I get to {id} from request without changing Order parameter to Long?
DomainClassConverter converts {id} to null as expected
My intention is to use order id in OrderNotFoundException
I can think of couple of ways.
1. Change the method to
#RequestMapping(path = "/payment", method = PUT)
ResponseEntity<?> submitPayment(#PathVariable("id") Order order, HttpServletRequest request) {
String path = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString()
// path will have "/payment/id". use substring or something similar to get just id
}
Write a #Around Aspect for #RequestMapping and get the argument
#Aspect
#Configuration
public class ControllerAspect {
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}
#Around("requestMapping()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String path = (String) joinPoint.getArgs()[0];
Object result= joinPoint.proceed();
}
}

Testing Spring's #RequestBody using Spring MockMVC

I am trying to test a method that posts an object to the database using Spring's MockMVC framework. I've constructed the test as follows:
#Test
public void testInsertObject() throws Exception {
String url = BASE_URL + "/object";
ObjectBean anObject = new ObjectBean();
anObject.setObjectId("33");
anObject.setUserId("4268321");
//... more
Gson gson = new Gson();
String json = gson.toJson(anObject);
MvcResult result = this.mockMvc.perform(
post(url)
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
.andReturn();
}
The method I'm testing uses Spring's #RequestBody to receive the ObjectBean, but the test always returns a 400 error.
#ResponseBody
#RequestMapping( consumes="application/json",
produces="application/json",
method=RequestMethod.POST,
value="/object")
public ObjectResponse insertObject(#RequestBody ObjectBean bean){
this.photonetService.insertObject(bean);
ObjectResponse response = new ObjectResponse();
response.setObject(bean);
return response;
}
The json created by gson in the test:
{
"objectId":"33",
"userId":"4268321",
//... many more
}
The ObjectBean class
public class ObjectBean {
private String objectId;
private String userId;
//... many more
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
//... many more
}
So my question is: how to I test this method using Spring MockMVC?
Use this one
public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void testInsertObject() throws Exception {
String url = BASE_URL + "/object";
ObjectBean anObject = new ObjectBean();
anObject.setObjectId("33");
anObject.setUserId("4268321");
//... more
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String requestJson=ow.writeValueAsString(anObject );
mockMvc.perform(post(url).contentType(APPLICATION_JSON_UTF8)
.content(requestJson))
.andExpect(status().isOk());
}
As described in the comments, this works because the object is converted to json and passed as the request body. Additionally, the contentType is defined as Json (APPLICATION_JSON_UTF8).
More info on the HTTP request body structure
the following works for me,
mockMvc.perform(
MockMvcRequestBuilders.post("/api/test/url")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(createItemForm)))
.andExpect(status().isCreated());
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
The issue is that you are serializing your bean with a custom Gson object while the application is attempting to deserialize your JSON with a Jackson ObjectMapper (within MappingJackson2HttpMessageConverter).
If you open up your server logs, you should see something like
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.util.Date from String value '2013-34-10-10:34:31': not a valid representation (error: Failed to parse Date value '2013-34-10-10:34:31': Can not parse date "2013-34-10-10:34:31": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
at [Source: java.io.StringReader#baea1ed; line: 1, column: 20] (through reference chain: com.spring.Bean["publicationDate"])
among other stack traces.
One solution is to set your Gson date format to one of the above (in the stacktrace).
The alternative is to register your own MappingJackson2HttpMessageConverter by configuring your own ObjectMapper to have the same date format as your Gson.
I have encountered a similar problem with a more recent version of Spring. I tried to use a new ObjectMapper().writeValueAsString(...) but it would not work in my case.
I actually had a String in a JSON format, but I feel like it is literally transforming the toString() method of every field into JSON. In my case, a date LocalDate field would end up as:
"date":{"year":2021,"month":"JANUARY","monthValue":1,"dayOfMonth":1,"chronology":{"id":"ISO","calendarType":"iso8601"},"dayOfWeek":"FRIDAY","leapYear":false,"dayOfYear":1,"era":"CE"}
which is not the best date format to send in a request ...
In the end, the simplest solution in my case is to use the Spring ObjectMapper. Its behaviour is better since it uses Jackson to build your JSON with complex types.
#Autowired
private ObjectMapper objectMapper;
and I simply used it in my test:
mockMvc.perform(post("/api/")
.content(objectMapper.writeValueAsString(...))
.contentType(MediaType.APPLICATION_JSON)
);

Return literal JSON strings in spring mvc #ResponseBody

I am storing objects in my database as JSON strings. I want to make a REST service that exposes these strings. When I write my methods however, the strings I get back have their quotes escaped. For example, I have included a method that returns a String,
#RequestMapping(value = "test", method = RequestMethod.GET)
public #ResponseBody
String getTest() {
return "{\"a\":1, \"b\":\"foo\"}";
}
but when I call this method in the browser I get a back "{\"a\":1, \"b\":\"foo\"}" when what I really want to happen is {"a": 1, "b": "foo"}. I think "String" as the return type is likely the problem, but what else can I do? A wrapper class does the same thing:
{
"value" : "{\"a\":1, \"b\":\"foo\"}"
}
I could serialize it and then return the object, but that seems a bit ridiculous.
Here is a possibly the relevant portion of my configuration file:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(mappingJacksonHttpMessageConverter());
}
#Bean
MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
mappingJacksonHttpMessageConverter.setObjectMapper(objectMapper);
mappingJacksonHttpMessageConverter.setPrettyPrint(true);
return mappingJacksonHttpMessageConverter;
}
Thanks
EDIT: as was suggested below, it seems the string is being double encoded. Commenting out the 2 classes in my configuration fixes this issue. However, I still have other places where I want to return Objects and would like to keep those running through that common serializing bean that I know where to configure. So I see my options as:
a) Do all the serializing myself. All methods return Strings, and those that are already JSON return themselves, and those that are objects all return JSONUtil.toJson(object). I don't love this approach, but I know it will work.
b) Use a wrapper class that looks kind of like:
public static class Wrapper {
#JsonRawValue
private final String value;
}
This leads to an awkward "value" at the front though that has no real meaning.
Basically what I want is #JsonRawValue, but to have it work on RequestMapping methods instead of properties.
Thoughts? Opinions? Other suggestions?
This works with Jackson 2 (at least):
#Controller
public class YourController {
#RequestMapping(..)
public #ResponseBody Json get() {
return new Json("{ \"attr\" : \"value\" }");
}
}
class Json {
private final String value;
public Json(String value) {
this.value = value;
}
#JsonValue
#JsonRawValue
public String value() {
return value;
}
}
Not particularly pretty but works. I only wish Spring supported this:
#RequestMapping(..)
public #JsonRawValue #ResponseBody String get() {
// ...
}
I guess what you want is producing a response with content-type application/json. In your case, when you have the json-data as a raw string, do the following:
In your controller add produces="application/json" to your #RequestMapping attribute:
#RequestMapping(value = "test", method = RequestMethod.GET, produces="application/json")
public #ResponseBody
String getTest() {
return "{\"a\":1, \"b\":\"foo\"}";
}
Then you have to configure the StringHttpMessageConverter to accept the application/json media-type.
With Java-config:
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
stringConverter.setSupportedMediaTypes(Arrays.asList( //
MediaType.TEXT_PLAIN, //
MediaType.TEXT_HTML, //
MediaType.APPLICATION_JSON));
converters.add(stringConverter);
}
With XML-config:
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<array>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json; charset=UTF-8" />
</bean>
</array>
</property>
</bean>
I used this:
#RequestMapping(..)
#ResponseBody
public JsonNode myGetRequest(){
...
//rawJsonString is the raw Json that we want to proxy back to the client
return objectMapper.readTree(rawJsonString);
}
And the Jackson converter knew how to transform the JsonNode into plain Json.
If you want to convert JSON String to JSON object in your browser, keep string convertor before Jackson convertor.
Follow this link for complete example. It works with custom converter configuration plus spring validation.
It Works
converters.add(stringConverter());
converters.add(mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
It Doesn't
converters.add(mappingJackson2HttpMessageConverter());
converters.add(stringConverter());
super.configureMessageConverters(converters);
In my case, I wanted the response type to be determined by a request parameter, so had to specify the content type in the code, e.g.:
#RequestMapping("/myurl")
public void radiusSearch(#RequestParam responseType, HttpServletResponse response) throws IOException {
String jsonResponse = makeSomeJson();
response.setContentType(responseType);
try {
response.getOutputStream().write(jsonResponse.getBytes());
} finally {
response.getOutputStream().close();
}
}
Today we had the same issue and solved it with multiple converters. Now every String will treated as a string and every other Object will get serialised by Jackson. This allows to serialise manually (by returning String) or automatically (by returning something else) in Spring controllers.
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(stringConverter());
converters.add(mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
#Bean
public StringHttpMessageConverter stringConverter() {
final StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(UTF_8);
stringConverter.setSupportedMediaTypes(Arrays.asList(
MediaType.TEXT_PLAIN,
MediaType.TEXT_HTML,
MediaType.APPLICATION_JSON));
return stringConverter;
}
#Bean
public GenericHttpMessageConverter<Object> mappingJackson2HttpMessageConverter() {
final ObjectMapper objectMapper = objectMapperBuilder().build();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);
return converter;
}
The \" means the character " is being escaped, which is standard. If it's being printed like that, you're probably double-serializing the object.
I know this is an old question, but I was just dealing with the opposite problem myself (I was returning a String and WANTED it to get converted to JSON). In your case, it sounds like you simply want to have your String treated as a plain string and not have any sort of JSON conversion done on it as you already have JSON.
So in your case you don't want to use the MappingJacksonHttpMessageConverter (or the MappingJackson2HttpMessageConverter if you're now using Jackson2). You want no conversions done at all, and that converter converts Java objects to/from JSON. So instead you should just use the plain StringHttpMessageConverter. You can do that by changing your setup method like this:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
}
This converter is applicable to */* types (the document is incorrect which says text/*, I found out the hard way in the debugger). So whether your content type is application/json or not, either way Spring won't mess with your Strings if you use this converter.
The solution to your problem is, this works perfectly without changing any configurations
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
JsonNode getTest() {
return JsonLoader.fromString("{\"a\":1, \"b\":\"foo\"}");
}

Resources