Index: johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java (revision ) +++ johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterTest.java (revision ) @@ -0,0 +1,152 @@ +/* + * 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. + */ +package org.apache.johnzon.mapper; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ObjectConverterTest { + + @Test + public void testObjectConverter() { + Contact contact = new Contact(); + contact.getLinkedPersons().addAll(Arrays.asList(new Person("f1", "l1"), new Person("f2", "l2"))); + + MapperBuilder mapperBuilder = new MapperBuilder(); + mapperBuilder.addPropertyEditor(Person.class, new PersonConverter()); + Mapper mapper = mapperBuilder.setAccessModeName("both").build(); + + String s = mapper.writeObjectAsString(contact); + Contact c = mapper.readObject(s, Contact.class); + String expected = "{\"linkedPersons\":[\"f1|l1\",\"f2|l2\"]}"; + Assert.assertEquals(expected, s); + Assert.assertEquals(contact, c); + } + + + public static class PersonConverter implements Converter { + @Override + public String toString(Person instance) { + if (instance == null) { + return null; + } + + return new StringBuilder(instance.getFirstName()).append("|").append(instance.getLastName()).toString(); + } + + @Override + public Person fromString(String text) { + if (text == null) { + return null; + } + String[] split = text.split("\\|"); + if (split != null && split.length == 2) { + Person p = new Person(split[0], split[1]); + return p; + } + + return null; + } + } + + public static class Contact { + private List linkedPersons = new ArrayList(); + + public List getLinkedPersons() { + return linkedPersons; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Contact)) { + return false; + } + + Contact contact = (Contact) o; + + if (linkedPersons != null ? !linkedPersons.equals(contact.linkedPersons) : contact.linkedPersons != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return linkedPersons != null ? linkedPersons.hashCode() : 0; + } + } + + public static class Person { + private String firstName; + private String lastName; + + //no default constructor on purpose + + private Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + @JohnzonConverter(PersonConverter.class) + public String getFirstName() { + return firstName; + } + + @JohnzonConverter(PersonConverter.class) + public String getLastName() { + return lastName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Person)) { + return false; + } + + Person person = (Person) o; + + if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) { + return false; + } + if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = firstName != null ? firstName.hashCode() : 0; + result = 31 * result + (lastName != null ? lastName.hashCode() : 0); + return result; + } + } +} Index: johnzon-mapper/src/test/java/org/apache/johnzon/mapper/EnumTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- johnzon-mapper/src/test/java/org/apache/johnzon/mapper/EnumTest.java (revision ) +++ johnzon-mapper/src/test/java/org/apache/johnzon/mapper/EnumTest.java (revision ) @@ -0,0 +1,176 @@ +/* + * 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. + */ +package org.apache.johnzon.mapper; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +public class EnumTest { + + @Test + public void testSimpleEnumAccessModeBoth() { + testSimpleField(new MapperBuilder().setAccessModeName("both") + .build()); + } + + @Test + public void testSimpleEnumAccessModeField() { + testSimpleField(new MapperBuilder().setAccessModeName("field") + .build()); + } + + private void testSimpleField(Mapper mapper) { + SimpleObject object = new SimpleObject(MyEnum.TWO); + + String objectAsString = mapper.writeObjectAsString(object); + Assert.assertEquals("{\"myEnum\":\"TWO\"}", objectAsString); + + Assert.assertEquals(object.myEnum, mapper.readObject(objectAsString, SimpleObject.class).myEnum); + } + + @Test + public void testSimpleEnumWithCollectionAccessModeBoth() { + testCollection(new MapperBuilder().setAccessModeName("both") + .build()); + } + + @Test + public void testSimpleEnumWithCollectionAccessModeField() { + testCollection(new MapperBuilder().setAccessModeName("field") + .build()); + } + + + private void testCollection(Mapper mapper) { + CollectionObject object = new CollectionObject(MyEnum.ONE, + MyEnum.TWO, + MyEnum.THREE, + MyEnum.TWO);// duplicate entry isn't a mistake + + String jsonString = "{\"enums\":[\"ONE\",\"TWO\",\"THREE\",\"TWO\"]}"; + + Assert.assertEquals(object.enums, mapper.readObject(jsonString, CollectionObject.class).enums); + + String johnzonString = mapper.writeObjectAsString(object); + Assert.assertEquals(jsonString, johnzonString); + Assert.assertEquals(object.enums, mapper.readObject(johnzonString, CollectionObject.class).enums); + } + + + @Test + public void testAdvancedEnumAccessModeBoth() { + testAdvancedEnum(new MapperBuilder().setAccessModeName("both") + .build()); + } + + @Test + public void testAdvancedEnumAccessModeField() { + testAdvancedEnum(new MapperBuilder().setAccessModeName("field") + .build()); + } + + private void testAdvancedEnum(Mapper mapper) { + AdvancedEnumObject object = new AdvancedEnumObject(AdvancedEnum.VALUE_1, Arrays.asList(AdvancedEnum.VALUE_2, + AdvancedEnum.VALUE_1, + AdvancedEnum.VALUE_1, + AdvancedEnum.VALUE_2)); + + String jsonString = "{\"advancedEnums\":[\"VALUE_2\",\"VALUE_1\",\"VALUE_1\",\"VALUE_2\"],\"advancedEnum\":\"VALUE_1\"}"; + + AdvancedEnumObject johnzonObject = mapper.readObject(jsonString, AdvancedEnumObject.class); + Assert.assertEquals(object.advancedEnum, johnzonObject.advancedEnum); + Assert.assertEquals(object.advancedEnums, johnzonObject.advancedEnums); + + String johnzonString = mapper.writeObjectAsString(object); + Assert.assertEquals(jsonString, johnzonString); + + johnzonObject = mapper.readObject(johnzonString, AdvancedEnumObject.class); + Assert.assertEquals(object.advancedEnum, johnzonObject.advancedEnum); + Assert.assertEquals(object.advancedEnums, johnzonObject.advancedEnums); + } + + + public enum MyEnum { + ONE, + TWO, + THREE + } + + public static class SimpleObject { + private MyEnum myEnum; + + private SimpleObject() { + } + + private SimpleObject(MyEnum myEnum) { + this.myEnum = myEnum; + } + } + + public static class CollectionObject { + private List enums; + + private CollectionObject() { + } + + private CollectionObject(MyEnum... enums) { + this.enums = Arrays.asList(enums); + } + } + + + public enum AdvancedEnum { + VALUE_1("one", 1), + VALUE_2("two", 2); + + private String string; + private int i; + + private AdvancedEnum(String string, int i) { + this.string = string; + this.i = i; + } + + + public String getString() { + return string; + } + + public int getI() { + return i; + } + } + + public static class AdvancedEnumObject { + private AdvancedEnum advancedEnum; + private List advancedEnums; + + private AdvancedEnumObject() { + } + + public AdvancedEnumObject(AdvancedEnum advancedEnum, List advancedEnums) { + this.advancedEnum = advancedEnum; + this.advancedEnums = advancedEnums; + } + } + +} Index: johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java (revision e63016c68b25b45d37da39535067079d864f3ea5) +++ johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java (revision ) @@ -418,6 +418,7 @@ return gen.writeEnd(); } else if (collection) { JsonGenerator gen = generator.writeStartArray(key); + for (final Object o : Collection.class.cast(value)) { gen = writeItem(gen, o); } @@ -441,6 +442,10 @@ private JsonGenerator writeItem(final JsonGenerator generator, final Object o) { JsonGenerator newGen = writePrimitives(generator, o); if (newGen == null) { + Converter converter = findConverter(o.getClass()); + if (converter != null) { + return writePrimitives(generator, doConvertFrom(o, (Converter) converter)); + } if (Collection.class.isInstance(o)) { newGen = doWriteArray(Collection.class.cast(o), generator); } else if (o != null && o.getClass().isArray()) { @@ -629,7 +634,7 @@ return objects; } - private Object toObject(final JsonValue jsonValue, final Type type) throws Exception { + private Object toObject(final JsonValue jsonValue, final Type type, final Converter converter) throws Exception { if (jsonValue == null || jsonValue == JsonValue.NULL) { return null; } @@ -707,12 +712,19 @@ if (type == BigDecimal.class) { return number.bigDecimalValue(); } - } else if (JsonString.class.isInstance(jsonValue) || Object.class == type) { + } else if ((JsonString.class.isInstance(jsonValue) || Object.class == type)) { + if(converter == null) { - return convertTo(Class.class.cast(type), JsonString.class.cast(jsonValue).getString()); + return convertTo(Class.class.cast(type), JsonString.class.cast(jsonValue).getString()); + } else { + return converter.fromString(JsonString.class.cast(jsonValue).getString()); - } + } + } throw new MapperException("Unable to parse " + jsonValue + " to " + type); } + private Object toObject(final JsonValue jsonValue, final Type type) throws Exception { + return toObject(jsonValue, type, null); + } private Object buildArray(final Type type, final JsonArray jsonArray) throws Exception { if (Class.class.isInstance(type)) { @@ -752,8 +764,10 @@ throw new IllegalStateException("not supported collection type: " + mapping.raw.getName()); } + Converter converter = converters.get(mapping.arg); + for (final JsonValue value : jsonArray) { - final Object element = toObject(value, mapping.arg); + final Object element = toObject(value, mapping.arg, converter); collection.add(element); } return collection;