blob: 38d2cc7d4a5ac863a0349f9b61303763bf2c124e [file] [log] [blame]
package org.apache.fulcrum.json.jackson;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.apache.avalon.framework.logger.Log4JLogger;
import org.apache.avalon.framework.logger.Logger;
import org.apache.fulcrum.json.JsonService;
import org.apache.fulcrum.json.jackson.example.Bean;
import org.apache.fulcrum.json.jackson.example.Rectangle;
import org.apache.fulcrum.json.jackson.example.TestClass;
import org.apache.fulcrum.json.jackson.mixins.BeanMixin;
import org.apache.fulcrum.json.jackson.mixins.RectangleMixin;
import org.apache.fulcrum.json.jackson.mixins.RectangleMixin2;
import org.apache.fulcrum.json.jackson.mixins.TypedRectangle;
import org.apache.fulcrum.testcontainer.BaseUnit5Test;
import org.apache.log4j.LogManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
/**
* Jackson2 JSON Test with EnabledDefaultTyping {@link DefaultTyping#OBJECT_AND_NON_CONCRETE}
*
* cft. http://wiki.fasterxml.com/JacksonPolymorphicDeserialization
*
* adding {@literal @}{@link RunWith} annotation with {@link JUnitPlatform} allows intellij/eclipse IDE recognize it's a test
*
* @author gk
* @version $Id$
*/
@RunWith(JUnitPlatform.class)
public class JacksonMapperEnabledDefaultTyping_OBJECT_AND_NON_CONCRETE_Test extends BaseUnit5Test {
private JsonService sc = null;
private final String preDefinedOutput = "{\"container\":{\"type\":\"java.util.HashMap\",\"cf\":\"Config.xml\"},\"configurationName\":\"Config.xml\",\"name\":\"mytest\"}";
Logger logger;
@BeforeEach
public void setUp() throws Exception {
sc = (JsonService) this.lookup(JsonService.ROLE);
logger = new Log4JLogger(LogManager.getLogger(getClass().getName()) );
((Jackson2MapperService) sc).getMapper().enableDefaultTypingAsProperty(
DefaultTyping.OBJECT_AND_NON_CONCRETE, "type");
}
@Test
public void testSerialize() throws Exception {
String serJson = sc.ser(new TestClass("mytest"));
assertEquals( preDefinedOutput, serJson, "Serialization failed ");
}
@Test
public void testDeSerialize() throws Exception {
String serJson = sc.ser(new TestClass("mytest"));
Object deson = sc.deSer(serJson, TestClass.class);
assertEquals(TestClass.class, deson.getClass(), "DeSer failed ");
}
@Test
public void testSerializeDateWithDefaultDateFormat() throws Exception {
Map<String, Date> map = new HashMap<String, Date>();
map.put("date", Calendar.getInstance().getTime());
String serJson = sc.ser(map);
logger.debug("serJson:" +serJson);
assertEquals(
true,
serJson.matches(".*\"java.util.Date\",\"\\d\\d/\\d\\d/\\d{4}\".*"),
"Serialize with Adapater failed ");
}
@Test
public void testSerializeDeSerializeDate() throws Exception {
Map<String, Date> map = new HashMap<String, Date>();
Calendar sourceDate = Calendar.getInstance();
sourceDate.set(1999, 3, 10);
// logger.debug("sourceDate calendar:"+ sourceDate);
logger.debug("sourceDate date:"+ sourceDate.getTime());
logger.debug("sourceDate millisec:"+ sourceDate.getTime().getTime());
map.put("mydate",sourceDate.getTime());
map.put("mydate2",Calendar.getInstance().getTime());
// default dateformat dd/mm/yy -> day time will be cut off !(hh, mm)
// first serialize
String serJson0 = sc.ser(map, false);
String serJson = sc.ser(map, Map.class, false);
logger.debug("serJson:"+ serJson0);
assertEquals(serJson0, serJson);
//second deserialize
DateKeyMixin serObject =sc.deSer(serJson0, DateKeyMixin.class);
assertTrue(serObject.mydate instanceof Date);
logger.debug("resultDate millisec: " + ((Date)serObject.mydate).getTime() +" source:"+ sourceDate.getTime().getTime() );
// cleanup all values the mapper dateformat, which is MM/dd/yyyy, does not contain.
sourceDate.set(Calendar.HOUR, 0);
sourceDate.set(Calendar.MINUTE, 0);
sourceDate.set(Calendar.SECOND, 0);
sourceDate.set(Calendar.MILLISECOND, 0);
sourceDate.set(Calendar.HOUR_OF_DAY, 0);
assertEquals(
((Date)serObject.mydate).getTime(),sourceDate.getTime().getTime(),
"millisec of result and source date should be equal, after zeroing not used formatter values:: ");
}
// all values represented in format of the object, which would be serialized are conserved, while the others are nulled
@Test
public void testSerializeDeSerializeTZDate() throws Exception {
Map<String, Date> map = new HashMap<String, Date>();
Calendar sourceDate = Calendar.getInstance(TimeZone.getTimeZone("America/Montreal"));// UTC -5
sourceDate.set(1999, 3, 10, 11, 10); // set in Montreal Time this date and time
//this may be in "any" locale timezone, eg. 1999-04-10 17:10 PM MESZ
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a Z");
// shows date and time in locale time
logger.debug("sourceDate string:"+ sourceDate.getTime());
logger.debug("sourceDate format:"+df.format(sourceDate.getTime()));
// any "timezone" information is lost from the Calendar when converting it into a java.util.Date by calling getTime()
map.put("mydate",sourceDate.getTime());
sc.setDateFormat(df);
String serJson0 = sc.ser(map, false);
String serJson = sc.ser(map, Map.class, false);
logger.debug("serJson:"+ serJson0);
assertEquals(serJson0, serJson);
DateKeyMixin serObject =sc.deSer(serJson0
, DateKeyMixin.class);
logger.debug("resultDate (serialized) millisec: " +
((Date)serObject.mydate).getTime() +" source:"+ sourceDate.getTime().getTime() );
logger.debug("may not be equal: millisec(resultDate(string)):" + ((Date)serObject.mydate).getTime() +
" millisec(sourceDate):"+ sourceDate.getTime().getTime() );
// cleanup all values the mapper dateformat does not contain.
sourceDate.set(Calendar.SECOND, 0);
sourceDate.set(Calendar.MILLISECOND, 0);
assertEquals(
((Date)serObject.mydate).getTime(),sourceDate.getTime().getTime(),
"milliseconds of resultDate (serialized) should be equal, if properly set the. ");
}
// timezone handling example
@Test
public void testDeSerializeTZDate() throws Exception {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm Z");
sc.setDateFormat(df);
DateKeyMixin serObject =sc.deSer( "{\"mydate\":[\"java.util.Date\",\"1999-04-10 10:10 -0500\"]}"
, DateKeyMixin.class);
assertTrue(serObject.mydate instanceof Date);
// compare object
Calendar compareDate = Calendar.getInstance(TimeZone.getTimeZone("America/Montreal"));// UTC -5
compareDate.set(1999, 3, 10, 11, 10);
String compareDateFormatted = df.format(compareDate.getTime());
logger.debug("compareDate format: " + compareDateFormatted );
logger.debug("may not be equal: millisec(resultDate(string)):" + ((Date)serObject.mydate).getTime() +
" millisec(sourceDate):"+ compareDate.getTime().getTime() );
assertEquals( compareDateFormatted,
df.format(((Date)serObject.mydate).getTime()),
"format should be equal"
);
logger.debug("format in locale timezone (resultDate(string)):" + df.format(((Date)serObject.mydate).getTime()));
}
@Test
public void testSerializeWithCustomFilter() throws Exception {
Bean filteredBean = new Bean();
filteredBean.setName("joe");
String bean = sc.serializeOnlyFilter(filteredBean, "name");
assertEquals(
"{\"name\":\"joe\"}",
bean);
Rectangle filteredRectangle = new Rectangle(5, 10);
filteredRectangle.setName("jim");
String rectangle = sc.serializeOnlyFilter(filteredRectangle,
"w", "name");
assertEquals(
"{\"w\":5,\"name\":\"jim\"}", rectangle);
}
@Test
public void testSerializationCollectionWithFilter() throws Exception {
List<Bean> beanList = new ArrayList<Bean>();
for (int i = 0; i < 10; i++) {
Bean filteredBean = new Bean();
filteredBean.setName("joe" + i);
filteredBean.setAge(i);
beanList.add(filteredBean);
}
String result = sc.serializeOnlyFilter(beanList, Bean.class, "name",
"age");
assertEquals(
"[{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe0','age':0},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe1','age':1},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe2','age':2},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe3','age':3},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe4','age':4},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe5','age':5},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe6','age':6},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe7','age':7},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe8','age':8},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe9','age':9}]",
result.replace('"', '\''));
}
@Test
public void testDeserializationCollectionWithFilter() throws Exception {
List<Bean> beanList = new ArrayList<Bean>();
for (int i = 0; i < 10; i++) {
Bean filteredBean = new Bean();
filteredBean.setName("joe" + i);
filteredBean.setAge(i);
beanList.add(filteredBean);
}
String result = sc.serializeOnlyFilter(beanList, Bean.class, "name",
"age");
//System.out.println("res:"+result);
// could not use TypeReference as JSON string has no type set for array:
// com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected VALUE_STRING:
// need JSON String that contains type id (for subtype of java.util.Collection)
//
// -> need to use constructCollectionType
Class clazz = Class.forName("org.apache.fulcrum.json.jackson.example.Bean");
List<Bean> beanList2 = (List<Bean>)sc.deSerCollection(result, new ArrayList(),clazz);
assertTrue( beanList2.size() == 10);
for (Bean bean : beanList2) {
assertEquals( Bean.class, bean.getClass());
}
}
@Test
public void testDeserializationUnTypedCollectionWithFilter()
throws Exception {
List<Bean> beanList = new ArrayList<Bean>();
for (int i = 0; i < 10; i++) {
Bean filteredBean = new Bean();
filteredBean.setName("joe" + i);
filteredBean.setAge(i);
beanList.add(filteredBean);
}
String result = sc.serializeOnlyFilter(beanList, Bean.class, "name",
"age");
// could not use TypeReference as JSON string has no type set for array:
// Exception: need JSON String that contains type id (for subtype of java.util.List)
// -> need to use constructCollectionType
Class clazz = Class.forName("org.apache.fulcrum.json.jackson.example.Bean");
List<Bean> beanList2 = (List<Bean>)sc.deSerCollection(result, new ArrayList(),clazz);
//Object beanList2 = sc.deSer(result, List.class);
assertTrue( beanList2 instanceof List);
assertTrue( ((List) beanList2).size() == 10);
for (int i = 0; i < ((List) beanList2).size(); i++) {
assertTrue(
((List) beanList2).get(i) instanceof Bean);
assertTrue(((Bean) ((List) beanList2).get(i))
.getName().equals("joe" + i));
}
}
@Test
public void testSerializeWithMixin() throws Exception {
Rectangle filteredRectangle = new Rectangle(5, 10);
filteredRectangle.setName("jim");
String serRect = sc
.addAdapter("M4RMixin", Rectangle.class, RectangleMixin.class).ser(
filteredRectangle);
assertEquals( "{\"width\":5}", serRect);
}
@Test
public void testSerializeWith2Mixins() throws Exception {
Bean filteredBean = new Bean();
filteredBean.setName("joe");
Rectangle filteredRectangle = new Rectangle(5, 10);
filteredRectangle.setName("jim");
String serRect = sc.addAdapter("M4RMixin2", Rectangle.class,
RectangleMixin2.class).ser(filteredRectangle);
assertEquals( "{\"name\":\"jim\",\"width\":5}", serRect);
//
String bean = sc.addAdapter("M4RBeanMixin", Bean.class,
BeanMixin.class).ser(filteredBean);;
assertEquals(
"{\"name\":\"joe\"}",
bean);
}
@Test
public void testSerializeWithMixinAndFilter() throws Exception {
Bean filteredBean = new Bean();
filteredBean.setName("joe");
sc.addAdapter("M4RBeanMixin", Bean.class,
BeanMixin.class);
// profession was already set to ignore, does not change
String bean = sc.serializeOnlyFilter(filteredBean, Bean.class, "profession");
assertEquals(
"{}",
bean);
}
@Test
public void testSerializeWithUnregisteredMixinAndFilter() throws Exception {
Bean filteredBean = new Bean();
filteredBean.setName("joe");
sc.addAdapter("M4RBeanMixin", Bean.class,
BeanMixin.class)
.addAdapter("M4RBeanMixin", Bean.class,
null);
// now profession is used after cleaning adapter
String bean = sc.serializeOnlyFilter(filteredBean, Bean.class, "profession");
assertEquals(
"{\"profession\":\"\"}",
bean);
}
@Test
public void testMultipleSerializingWithMixinAndFilter() throws Exception {
Rectangle filteredRectangle = new Rectangle(5, 10);
filteredRectangle.setName("jim");
sc.addAdapter("M4RMixin2", Rectangle.class,
RectangleMixin2.class);
// if serialization is done Jackson clean cache
String rectangle0 = sc.ser(filteredRectangle,Rectangle.class,true);
assertEquals(
"{\"name\":\"jim\",\"width\":5}",
rectangle0);
// filtering out name, using width from mixin2 as a second filter
String rectangle = sc.serializeOnlyFilter(filteredRectangle, Rectangle.class, true, "width");
assertEquals(
"{\"width\":5}",
rectangle);
// default for mixin
String rectangle1 = sc.ser(filteredRectangle);
assertEquals(
"{\"name\":\"jim\",\"width\":5}",
rectangle1);
}
@Test
public void testSerializationCollectionWithMixin() throws Exception {
List<Bean> beanList = new ArrayList<Bean>();
for (int i = 0; i < 10; i++) {
Bean filteredBean = new Bean();
filteredBean.setName("joe" + i);
filteredBean.setAge(i);
beanList.add(filteredBean);
}
String result = sc.addAdapter("M4RMixin", Bean.class, BeanMixin.class)
.ser(beanList);
assertEquals(
"[{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe0'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe1'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe2'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe3'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe4'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe5'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe6'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe7'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe8'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe9'}]",
result.replace('"', '\''));
}
@Test
public void testDeSerializationCollectionWithMixin() throws Exception {
List<Bean> beanList = new ArrayList<Bean>();
for (int i = 0; i < 10; i++) {
Bean filteredBean = new Bean();
filteredBean.setName("joe" + i);
filteredBean.setAge(i);
beanList.add(filteredBean);
}
String result = sc.addAdapter("M4RMixin", Bean.class, BeanMixin.class)
.ser(beanList);
logger.debug("result:::"+ result);
// Type List.class / TypeReference -> Exception: need JSON String that contains type id (for subtype of java.util.List)
// Type: Bean.class -> Exception: Can not deserialize instance of org.apache.fulcrum.json.jackson.example.Bean out of START_ARRAY token
// -> need to use constructCollectionType
List<Bean> beanList2 = (List<Bean>)sc.deSerCollection(result, new ArrayList(),Bean.class);
assertTrue( beanList2.size() == 10);
for (Bean bean : beanList2) {
assertEquals( Bean.class, bean.getClass());
}
}
@Test
public void testSerializationCollectionWithMixins() throws Exception {
List components = new ArrayList<Object>();
components.add(new Rectangle(25, 3));
components.add(new Rectangle(250, 30));
for (int i = 0; i < 3; i++) {
Bean filteredBean = new Bean();
filteredBean.setName("joe" + i);
filteredBean.setAge(i);
components.add(filteredBean);
}
// property w->width, BeanMixin: name ignore other properties
sc.addAdapter("M4RMixin", Rectangle.class, RectangleMixin.class).addAdapter(
"M4BeanRMixin", Bean.class, BeanMixin.class);
String serRect = sc.ser(components);
assertEquals(
"[{'type':'org.apache.fulcrum.json.jackson.example.Rectangle','width':25},{'type':'org.apache.fulcrum.json.jackson.example.Rectangle','width':250},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe0'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe1'},{'type':'org.apache.fulcrum.json.jackson.example.Bean','name':'joe2'}]",
serRect.replace('"', '\''),
"Serialization failed ");
}
@Test
public void testSerializeCollectionWithOnlyFilter() throws Exception {
List<TypedRectangle> rectList = new ArrayList<TypedRectangle>();
for (int i = 0; i < 2; i++) {
TypedRectangle filteredRect = new TypedRectangle(i, i, "rect" + i);
rectList.add(filteredRect);
}
assertEquals("[{\"type\":\"org.apache.fulcrum.json.jackson.mixins.TypedRectangle\",\"w\":0},{\"type\":\"org.apache.fulcrum.json.jackson.mixins.TypedRectangle\",\"w\":1}]",
sc.serializeOnlyFilter(rectList, TypedRectangle.class, true, "w"));
}
public static class DateKeyMixin {
// @JsonCreator
// static Object create(Map<String, Object> map) {
// return map; //map.get("date");
// //return map.get("date");
// }
public Object mydate;
public Object mydate2;
}
}