Consistent object type exposure for JSON rendering (workaround for Gson)
Issue: SPR-16461
This commit is contained in:
parent
31b25b5808
commit
817a836960
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -87,21 +87,16 @@ public abstract class AbstractGenericHttpMessageConverter<T> extends AbstractHtt
|
||||||
|
|
||||||
if (outputMessage instanceof StreamingHttpOutputMessage) {
|
if (outputMessage instanceof StreamingHttpOutputMessage) {
|
||||||
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
|
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
|
||||||
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
|
streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(final OutputStream outputStream) throws IOException {
|
public OutputStream getBody() {
|
||||||
writeInternal(t, type, new HttpOutputMessage() {
|
|
||||||
@Override
|
|
||||||
public OutputStream getBody() throws IOException {
|
|
||||||
return outputStream;
|
return outputStream;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public HttpHeaders getHeaders() {
|
public HttpHeaders getHeaders() {
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
writeInternal(t, type, outputMessage);
|
writeInternal(t, type, outputMessage);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.http.converter.json;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
@ -34,7 +35,7 @@ import org.springframework.util.Assert;
|
||||||
* By default, it supports {@code application/json} and {@code application/*+json} with
|
* By default, it supports {@code application/json} and {@code application/*+json} with
|
||||||
* {@code UTF-8} character set.
|
* {@code UTF-8} character set.
|
||||||
*
|
*
|
||||||
* <p>Tested against Gson 2.6; compatible with Gson 2.0 and higher.
|
* <p>Tested against Gson 2.8; compatible with Gson 2.0 and higher.
|
||||||
*
|
*
|
||||||
* @author Roy Clarkson
|
* @author Roy Clarkson
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
|
@ -93,7 +94,12 @@ public class GsonHttpMessageConverter extends AbstractJsonHttpMessageConverter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception {
|
protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception {
|
||||||
if (type != null) {
|
// In Gson, toJson with a type argument will exclusively use that given type,
|
||||||
|
// ignoring the actual type of the object... which might be more specific,
|
||||||
|
// e.g. a subclass of the specified type which includes additional fields.
|
||||||
|
// As a consequence, we're only passing in parameterized type declarations
|
||||||
|
// which might contain extra generics that the object instance doesn't retain.
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
getGson().toJson(o, type, writer);
|
getGson().toJson(o, type, writer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.http.converter.json;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import javax.json.bind.Jsonb;
|
import javax.json.bind.Jsonb;
|
||||||
import javax.json.bind.JsonbBuilder;
|
import javax.json.bind.JsonbBuilder;
|
||||||
|
|
@ -100,7 +101,7 @@ public class JsonbHttpMessageConverter extends AbstractJsonHttpMessageConverter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception {
|
protected void writeInternal(Object o, @Nullable Type type, Writer writer) throws Exception {
|
||||||
if (type != null) {
|
if (type instanceof ParameterizedType) {
|
||||||
getJsonb().toJson(o, type, writer);
|
getJsonb().toJson(o, type, writer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -27,6 +27,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
@ -40,6 +41,7 @@ import static org.junit.Assert.*;
|
||||||
* Gson 2.x converter tests.
|
* Gson 2.x converter tests.
|
||||||
*
|
*
|
||||||
* @author Roy Clarkson
|
* @author Roy Clarkson
|
||||||
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public class GsonHttpMessageConverterTests {
|
public class GsonHttpMessageConverterTests {
|
||||||
|
|
||||||
|
|
@ -129,6 +131,29 @@ public class GsonHttpMessageConverterTests {
|
||||||
outputMessage.getHeaders().getContentType());
|
outputMessage.getHeaders().getContentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeWithBaseType() throws IOException {
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
MyBean body = new MyBean();
|
||||||
|
body.setString("Foo");
|
||||||
|
body.setNumber(42);
|
||||||
|
body.setFraction(42F);
|
||||||
|
body.setArray(new String[] {"Foo", "Bar"});
|
||||||
|
body.setBool(true);
|
||||||
|
body.setBytes(new byte[] {0x1, 0x2});
|
||||||
|
this.converter.write(body, MyBase.class, null, outputMessage);
|
||||||
|
Charset utf8 = StandardCharsets.UTF_8;
|
||||||
|
String result = outputMessage.getBodyAsString(utf8);
|
||||||
|
assertTrue(result.contains("\"string\":\"Foo\""));
|
||||||
|
assertTrue(result.contains("\"number\":42"));
|
||||||
|
assertTrue(result.contains("fraction\":42.0"));
|
||||||
|
assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]"));
|
||||||
|
assertTrue(result.contains("\"bool\":true"));
|
||||||
|
assertTrue(result.contains("\"bytes\":[1,2]"));
|
||||||
|
assertEquals("Invalid content-type", new MediaType("application", "json", utf8),
|
||||||
|
outputMessage.getHeaders().getContentType());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeUTF16() throws IOException {
|
public void writeUTF16() throws IOException {
|
||||||
MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE);
|
MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE);
|
||||||
|
|
@ -149,7 +174,7 @@ public class GsonHttpMessageConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void readGenerics() throws Exception {
|
public void readAndWriteGenerics() throws Exception {
|
||||||
Field beansList = ListHolder.class.getField("listField");
|
Field beansList = ListHolder.class.getField("listField");
|
||||||
|
|
||||||
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
||||||
|
|
@ -167,11 +192,15 @@ public class GsonHttpMessageConverterTests {
|
||||||
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
assertTrue(result.isBool());
|
assertTrue(result.isBool());
|
||||||
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, genericType, new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void readParameterizedType() throws Exception {
|
public void readAndWriteParameterizedType() throws Exception {
|
||||||
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {
|
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -189,10 +218,40 @@ public class GsonHttpMessageConverterTests {
|
||||||
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
assertTrue(result.isBool());
|
assertTrue(result.isBool());
|
||||||
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, beansList.getType(), new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void prefixJson() throws Exception {
|
@SuppressWarnings("unchecked")
|
||||||
|
public void writeParameterizedBaseType() throws Exception {
|
||||||
|
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {};
|
||||||
|
ParameterizedTypeReference<List<MyBase>> baseList = new ParameterizedTypeReference<List<MyBase>>() {};
|
||||||
|
|
||||||
|
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
||||||
|
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]";
|
||||||
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8));
|
||||||
|
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
|
||||||
|
|
||||||
|
List<MyBean> results = (List<MyBean>) converter.read(beansList.getType(), null, inputMessage);
|
||||||
|
assertEquals(1, results.size());
|
||||||
|
MyBean result = results.get(0);
|
||||||
|
assertEquals("Foo", result.getString());
|
||||||
|
assertEquals(42, result.getNumber());
|
||||||
|
assertEquals(42F, result.getFraction(), 0F);
|
||||||
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
|
assertTrue(result.isBool());
|
||||||
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, baseList.getType(), new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prefixJson() throws IOException {
|
||||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
this.converter.setPrefixJson(true);
|
this.converter.setPrefixJson(true);
|
||||||
this.converter.writeInternal("foo", null, outputMessage);
|
this.converter.writeInternal("foo", null, outputMessage);
|
||||||
|
|
@ -200,7 +259,7 @@ public class GsonHttpMessageConverterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void prefixJsonCustom() throws Exception {
|
public void prefixJsonCustom() throws IOException {
|
||||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
this.converter.setJsonPrefix(")))");
|
this.converter.setJsonPrefix(")))");
|
||||||
this.converter.writeInternal("foo", null, outputMessage);
|
this.converter.writeInternal("foo", null, outputMessage);
|
||||||
|
|
@ -208,10 +267,22 @@ public class GsonHttpMessageConverterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class MyBean {
|
public static class MyBase {
|
||||||
|
|
||||||
private String string;
|
private String string;
|
||||||
|
|
||||||
|
public String getString() {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setString(String string) {
|
||||||
|
this.string = string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MyBean extends MyBase {
|
||||||
|
|
||||||
private int number;
|
private int number;
|
||||||
|
|
||||||
private float fraction;
|
private float fraction;
|
||||||
|
|
@ -222,30 +293,6 @@ public class GsonHttpMessageConverterTests {
|
||||||
|
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBytes(byte[] bytes) {
|
|
||||||
this.bytes = bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBool() {
|
|
||||||
return bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBool(boolean bool) {
|
|
||||||
this.bool = bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString() {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setString(String string) {
|
|
||||||
this.string = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumber() {
|
public int getNumber() {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
@ -269,6 +316,22 @@ public class GsonHttpMessageConverterTests {
|
||||||
public void setArray(String[] array) {
|
public void setArray(String[] array) {
|
||||||
this.array = array;
|
this.array = array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBool() {
|
||||||
|
return bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBool(boolean bool) {
|
||||||
|
this.bool = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package org.springframework.http.converter.json;
|
package org.springframework.http.converter.json;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -25,6 +27,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
@ -128,6 +131,29 @@ public class JsonbHttpMessageConverterTests {
|
||||||
outputMessage.getHeaders().getContentType());
|
outputMessage.getHeaders().getContentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeWithBaseType() throws IOException {
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
MyBean body = new MyBean();
|
||||||
|
body.setString("Foo");
|
||||||
|
body.setNumber(42);
|
||||||
|
body.setFraction(42F);
|
||||||
|
body.setArray(new String[] {"Foo", "Bar"});
|
||||||
|
body.setBool(true);
|
||||||
|
body.setBytes(new byte[] {0x1, 0x2});
|
||||||
|
this.converter.write(body, MyBase.class, null, outputMessage);
|
||||||
|
Charset utf8 = StandardCharsets.UTF_8;
|
||||||
|
String result = outputMessage.getBodyAsString(utf8);
|
||||||
|
assertTrue(result.contains("\"string\":\"Foo\""));
|
||||||
|
assertTrue(result.contains("\"number\":42"));
|
||||||
|
assertTrue(result.contains("fraction\":42.0"));
|
||||||
|
assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]"));
|
||||||
|
assertTrue(result.contains("\"bool\":true"));
|
||||||
|
assertTrue(result.contains("\"bytes\":[1,2]"));
|
||||||
|
assertEquals("Invalid content-type", new MediaType("application", "json", utf8),
|
||||||
|
outputMessage.getHeaders().getContentType());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeUTF16() throws IOException {
|
public void writeUTF16() throws IOException {
|
||||||
MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE);
|
MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE);
|
||||||
|
|
@ -148,9 +174,34 @@ public class JsonbHttpMessageConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void readParameterizedType() throws IOException {
|
public void readAndWriteGenerics() throws Exception {
|
||||||
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {
|
Field beansList = ListHolder.class.getField("listField");
|
||||||
};
|
|
||||||
|
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
||||||
|
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]";
|
||||||
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8));
|
||||||
|
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
|
||||||
|
|
||||||
|
Type genericType = beansList.getGenericType();
|
||||||
|
List<MyBean> results = (List<MyBean>) converter.read(genericType, MyBeanListHolder.class, inputMessage);
|
||||||
|
assertEquals(1, results.size());
|
||||||
|
MyBean result = results.get(0);
|
||||||
|
assertEquals("Foo", result.getString());
|
||||||
|
assertEquals(42, result.getNumber());
|
||||||
|
assertEquals(42F, result.getFraction(), 0F);
|
||||||
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
|
assertTrue(result.isBool());
|
||||||
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, genericType, new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void readAndWriteParameterizedType() throws Exception {
|
||||||
|
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {};
|
||||||
|
|
||||||
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
||||||
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]";
|
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]";
|
||||||
|
|
@ -166,13 +217,71 @@ public class JsonbHttpMessageConverterTests {
|
||||||
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
assertTrue(result.isBool());
|
assertTrue(result.isBool());
|
||||||
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, beansList.getType(), new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void writeParameterizedBaseType() throws Exception {
|
||||||
|
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {};
|
||||||
|
ParameterizedTypeReference<List<MyBase>> baseList = new ParameterizedTypeReference<List<MyBase>>() {};
|
||||||
|
|
||||||
|
String body = "[{\"bytes\":[1,2],\"array\":[\"Foo\",\"Bar\"]," +
|
||||||
|
"\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}]";
|
||||||
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8));
|
||||||
|
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
|
||||||
|
|
||||||
|
List<MyBean> results = (List<MyBean>) converter.read(beansList.getType(), null, inputMessage);
|
||||||
|
assertEquals(1, results.size());
|
||||||
|
MyBean result = results.get(0);
|
||||||
|
assertEquals("Foo", result.getString());
|
||||||
|
assertEquals(42, result.getNumber());
|
||||||
|
assertEquals(42F, result.getFraction(), 0F);
|
||||||
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
|
assertTrue(result.isBool());
|
||||||
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, baseList.getType(), new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prefixJson() throws IOException {
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
this.converter.setPrefixJson(true);
|
||||||
|
this.converter.writeInternal("foo", null, outputMessage);
|
||||||
|
assertEquals(")]}', foo", outputMessage.getBodyAsString(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void prefixJsonCustom() throws IOException {
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
this.converter.setJsonPrefix(")))");
|
||||||
|
this.converter.writeInternal("foo", null, outputMessage);
|
||||||
|
assertEquals(")))foo", outputMessage.getBodyAsString(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class MyBean {
|
public static class MyBase {
|
||||||
|
|
||||||
private String string;
|
private String string;
|
||||||
|
|
||||||
|
public String getString() {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setString(String string) {
|
||||||
|
this.string = string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MyBean extends MyBase {
|
||||||
|
|
||||||
private int number;
|
private int number;
|
||||||
|
|
||||||
private float fraction;
|
private float fraction;
|
||||||
|
|
@ -183,30 +292,6 @@ public class JsonbHttpMessageConverterTests {
|
||||||
|
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBytes(byte[] bytes) {
|
|
||||||
this.bytes = bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBool() {
|
|
||||||
return bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBool(boolean bool) {
|
|
||||||
this.bool = bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString() {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setString(String string) {
|
|
||||||
this.string = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumber() {
|
public int getNumber() {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
@ -230,6 +315,32 @@ public class JsonbHttpMessageConverterTests {
|
||||||
public void setArray(String[] array) {
|
public void setArray(String[] array) {
|
||||||
this.array = array;
|
this.array = array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBool() {
|
||||||
|
return bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBool(boolean bool) {
|
||||||
|
this.bool = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ListHolder<E> {
|
||||||
|
|
||||||
|
public List<E> listField;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MyBeanListHolder extends ListHolder<MyBean> {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ser.FilterProvider;
|
||||||
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
|
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
|
||||||
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.skyscreamer.jsonassert.JSONAssert;
|
||||||
|
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
@ -41,21 +42,15 @@ import org.springframework.http.converter.HttpMessageConversionException;
|
||||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.hamcrest.CoreMatchers.endsWith;
|
import static org.junit.Assert.*;
|
||||||
import static org.hamcrest.CoreMatchers.not;
|
|
||||||
import static org.hamcrest.CoreMatchers.startsWith;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jackson 2.x converter tests.
|
* Jackson 2.x converter tests.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public class MappingJackson2HttpMessageConverterTests {
|
public class MappingJackson2HttpMessageConverterTests {
|
||||||
|
|
||||||
|
|
@ -148,6 +143,28 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
outputMessage.getHeaders().getContentType());
|
outputMessage.getHeaders().getContentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeWithBaseType() throws IOException {
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
MyBean body = new MyBean();
|
||||||
|
body.setString("Foo");
|
||||||
|
body.setNumber(42);
|
||||||
|
body.setFraction(42F);
|
||||||
|
body.setArray(new String[] {"Foo", "Bar"});
|
||||||
|
body.setBool(true);
|
||||||
|
body.setBytes(new byte[] {0x1, 0x2});
|
||||||
|
converter.write(body, MyBase.class, null, outputMessage);
|
||||||
|
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
|
||||||
|
assertTrue(result.contains("\"string\":\"Foo\""));
|
||||||
|
assertTrue(result.contains("\"number\":42"));
|
||||||
|
assertTrue(result.contains("fraction\":42.0"));
|
||||||
|
assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]"));
|
||||||
|
assertTrue(result.contains("\"bool\":true"));
|
||||||
|
assertTrue(result.contains("\"bytes\":\"AQI=\""));
|
||||||
|
assertEquals("Invalid content-type", new MediaType("application", "json", StandardCharsets.UTF_8),
|
||||||
|
outputMessage.getHeaders().getContentType());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeUTF16() throws IOException {
|
public void writeUTF16() throws IOException {
|
||||||
MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE);
|
MediaType contentType = new MediaType("application", "json", StandardCharsets.UTF_16BE);
|
||||||
|
|
@ -177,7 +194,7 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void readGenerics() throws IOException {
|
public void readAndWriteGenerics() throws Exception {
|
||||||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter() {
|
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter() {
|
||||||
@Override
|
@Override
|
||||||
protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
|
protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
|
||||||
|
|
@ -208,11 +225,15 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
assertTrue(result.isBool());
|
assertTrue(result.isBool());
|
||||||
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void readParameterizedType() throws IOException {
|
public void readAndWriteParameterizedType() throws Exception {
|
||||||
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {};
|
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {};
|
||||||
|
|
||||||
String body = "[{" +
|
String body = "[{" +
|
||||||
|
|
@ -235,8 +256,43 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
assertTrue(result.isBool());
|
assertTrue(result.isBool());
|
||||||
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, beansList.getType(), new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void writeParameterizedBaseType() throws Exception {
|
||||||
|
ParameterizedTypeReference<List<MyBean>> beansList = new ParameterizedTypeReference<List<MyBean>>() {};
|
||||||
|
ParameterizedTypeReference<List<MyBase>> baseList = new ParameterizedTypeReference<List<MyBase>>() {};
|
||||||
|
|
||||||
|
String body = "[{" +
|
||||||
|
"\"bytes\":\"AQI=\"," +
|
||||||
|
"\"array\":[\"Foo\",\"Bar\"]," +
|
||||||
|
"\"number\":42," +
|
||||||
|
"\"string\":\"Foo\"," +
|
||||||
|
"\"bool\":true," +
|
||||||
|
"\"fraction\":42.0}]";
|
||||||
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8"));
|
||||||
|
inputMessage.getHeaders().setContentType(new MediaType("application", "json"));
|
||||||
|
|
||||||
|
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
|
||||||
|
List<MyBean> results = (List<MyBean>) converter.read(beansList.getType(), null, inputMessage);
|
||||||
|
assertEquals(1, results.size());
|
||||||
|
MyBean result = results.get(0);
|
||||||
|
assertEquals("Foo", result.getString());
|
||||||
|
assertEquals(42, result.getNumber());
|
||||||
|
assertEquals(42F, result.getFraction(), 0F);
|
||||||
|
assertArrayEquals(new String[] {"Foo", "Bar"}, result.getArray());
|
||||||
|
assertTrue(result.isBool());
|
||||||
|
assertArrayEquals(new byte[] {0x1, 0x2}, result.getBytes());
|
||||||
|
|
||||||
|
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||||
|
converter.write(results, baseList.getType(), new MediaType("application", "json"), outputMessage);
|
||||||
|
JSONAssert.assertEquals(body, outputMessage.getBodyAsString(StandardCharsets.UTF_8), true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void prettyPrint() throws Exception {
|
public void prettyPrint() throws Exception {
|
||||||
|
|
@ -433,10 +489,22 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class MyBean implements MyInterface {
|
public static class MyBase implements MyInterface{
|
||||||
|
|
||||||
private String string;
|
private String string;
|
||||||
|
|
||||||
|
public String getString() {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setString(String string) {
|
||||||
|
this.string = string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class MyBean extends MyBase {
|
||||||
|
|
||||||
private int number;
|
private int number;
|
||||||
|
|
||||||
private float fraction;
|
private float fraction;
|
||||||
|
|
@ -447,30 +515,6 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
|
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBytes(byte[] bytes) {
|
|
||||||
this.bytes = bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBool() {
|
|
||||||
return bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBool(boolean bool) {
|
|
||||||
this.bool = bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString() {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setString(String string) {
|
|
||||||
this.string = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumber() {
|
public int getNumber() {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
@ -494,6 +538,22 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||||
public void setArray(String[] array) {
|
public void setArray(String[] array) {
|
||||||
this.array = array;
|
this.array = array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBool() {
|
||||||
|
return bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBool(boolean bool) {
|
||||||
|
this.bool = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue