1 package com.fasterxml.jackson.databind.struct; 2 3 import java.io.IOException; 4 import java.util.Map; 5 import java.util.TreeMap; 6 7 import com.fasterxml.jackson.annotation.*; 8 9 import com.fasterxml.jackson.databind.*; 10 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 12 // Unit test for [JACKSON-890] 13 public class TestBackRefsWithPolymorphic extends BaseMapTest 14 { 15 @JsonPropertyOrder(alphabetic=true) 16 interface Entity 17 { getEntityType()18 @JsonIgnore String getEntityType(); getId()19 Long getId(); setId(Long id)20 void setId(Long id); setPersistable()21 @JsonIgnore void setPersistable(); 22 } 23 24 @JsonDeserialize(as = NestedPropertySheetImpl.class) 25 interface NestedPropertySheet 26 extends Property<PropertySheet> 27 { getValue()28 @Override PropertySheet getValue(); setValue(PropertySheet propertySheet)29 void setValue(PropertySheet propertySheet); 30 } 31 32 @JsonDeserialize(as = AbstractProperty.class) 33 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, 34 include = JsonTypeInfo.As.PROPERTY, 35 property = "@class") 36 interface Property<T> extends Entity 37 { getName()38 String getName(); getParentSheet()39 PropertySheet getParentSheet(); getValue()40 T getValue(); setName(String name)41 void setName(String name); setParentSheet(PropertySheet parentSheet)42 void setParentSheet(PropertySheet parentSheet); 43 } 44 45 @JsonDeserialize(as = PropertySheetImpl.class) 46 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, 47 include = JsonTypeInfo.As.PROPERTY, 48 property = "@class") 49 @SuppressWarnings("rawtypes") 50 interface PropertySheet extends Entity 51 { addProperty(Property property)52 void addProperty(Property property); getProperties()53 Map<String, Property> getProperties(); setProperties(Map<String, Property> properties)54 void setProperties(Map<String, Property> properties); 55 } 56 57 @JsonDeserialize(as = StringPropertyImpl.class) 58 interface StringProperty 59 extends Property<String> 60 { getValue()61 @Override String getValue(); setValue(String value)62 void setValue(String value); 63 } 64 65 static class AbstractEntity implements Entity 66 { 67 private long id; 68 getEntityType()69 @Override public String getEntityType() { 70 return ""; 71 } 72 getId()73 @Override public Long getId() { 74 return id; 75 } 76 setId(Long id)77 @Override public void setId(Long id) 78 { 79 this.id = id; 80 } 81 setPersistable()82 @Override public void setPersistable() { } 83 } 84 85 abstract static class AbstractProperty<T> 86 extends AbstractEntity 87 implements Property<T> 88 { 89 private String m_name; 90 private PropertySheet m_parentSheet; 91 AbstractProperty()92 protected AbstractProperty() { } 93 AbstractProperty(String name)94 protected AbstractProperty(String name) { 95 m_name = name; 96 } 97 getName()98 @Override public String getName() { 99 return m_name; 100 } 101 102 @JsonBackReference("propertySheet-properties") getParentSheet()103 @Override public PropertySheet getParentSheet() { 104 return m_parentSheet; 105 } 106 setName(String name)107 @Override public void setName(String name) { 108 m_name = name; 109 } 110 setParentSheet(PropertySheet parentSheet)111 @Override public void setParentSheet(PropertySheet parentSheet) { 112 m_parentSheet = parentSheet; 113 } 114 } 115 116 @JsonPropertyOrder(alphabetic=true) 117 static class NestedPropertySheetImpl 118 extends AbstractProperty<PropertySheet> 119 implements NestedPropertySheet 120 { 121 private PropertySheet m_propertySheet; 122 NestedPropertySheetImpl(String name, PropertySheet propertySheet)123 protected NestedPropertySheetImpl(String name, 124 PropertySheet propertySheet) 125 { 126 super(name); 127 m_propertySheet = propertySheet; 128 } 129 NestedPropertySheetImpl()130 NestedPropertySheetImpl() { } 131 getValue()132 @Override public PropertySheet getValue() { 133 return m_propertySheet; 134 } 135 setValue(PropertySheet propertySheet)136 @Override public void setValue(PropertySheet propertySheet) { 137 m_propertySheet = propertySheet; 138 } 139 } 140 141 @SuppressWarnings("rawtypes") 142 static class PropertySheetImpl 143 extends AbstractEntity 144 implements PropertySheet 145 { 146 private Map<String, Property> m_properties; 147 addProperty(Property property)148 @Override public void addProperty(Property property) 149 { 150 if (m_properties == null) { 151 m_properties = new TreeMap<String, Property>(); 152 } 153 property.setParentSheet(this); 154 m_properties.put(property.getName(), property); 155 } 156 157 @JsonDeserialize(as = TreeMap.class, 158 keyAs = String.class, 159 contentAs = Property.class) 160 @JsonManagedReference("propertySheet-properties") getProperties()161 @Override public Map<String, Property> getProperties() { 162 return m_properties; 163 } 164 setProperties(Map<String, Property> properties)165 @Override public void setProperties(Map<String, Property> properties) { 166 m_properties = properties; 167 } 168 } 169 170 static class StringPropertyImpl 171 extends AbstractProperty<String> 172 implements StringProperty 173 { 174 private String m_value; 175 StringPropertyImpl(String name, String value)176 public StringPropertyImpl(String name, String value) { 177 super(name); 178 m_value = value; 179 } 180 StringPropertyImpl()181 StringPropertyImpl() { } 182 getValue()183 @Override public String getValue() { 184 return m_value; 185 } 186 setValue(String value)187 @Override public void setValue(String value) { 188 m_value = value; 189 } 190 } 191 192 static class YetAnotherClass extends StringPropertyImpl { } 193 194 /* 195 /********************************************************** 196 /* Test methods 197 /********************************************************** 198 */ 199 200 private final String CLASS_NAME = getClass().getName(); 201 // NOTE: order is arbitrary, test is fragile... has to work for now 202 private final String JSON = 203 "{\"@class\":\""+CLASS_NAME+"$PropertySheetImpl\",\"id\":0,\"properties\":{\"p1name\":{\"@class\":" 204 +"\"" +CLASS_NAME+ "$StringPropertyImpl\",\"id\":0,\"name\":\"p1name\",\"value\":\"p1value\"}," 205 +"\"p2name\":{\"@class\":\""+CLASS_NAME+"$StringPropertyImpl\",\"id\":0," 206 +"\"name\":\"p2name\",\"value\":\"p2value\"}}}"; 207 208 private final ObjectMapper MAPPER = new ObjectMapper(); 209 testDeserialize()210 public void testDeserialize() throws IOException 211 { 212 PropertySheet input = MAPPER.readValue(JSON, PropertySheet.class); 213 assertEquals(JSON, MAPPER.writeValueAsString(input)); 214 } 215 testSerialize()216 public void testSerialize() throws IOException 217 { 218 PropertySheet sheet = new PropertySheetImpl(); 219 220 sheet.addProperty(new StringPropertyImpl("p1name", "p1value")); 221 sheet.addProperty(new StringPropertyImpl("p2name", "p2value")); 222 String actual = MAPPER.writeValueAsString(sheet); 223 assertEquals(JSON, actual); 224 } 225 } 226