diff --git a/pom.xml b/pom.xml
index a4a654d29..7b36cd736 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,7 @@
org.avaje.ebean
ebean-annotation
- 1.2
+ 1.3
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
index 0036f68af..d11fe0494 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -213,6 +213,8 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
private final boolean draftableElement;
+ private final BeanProperty unmappedJson;
+
private final BeanProperty draft;
private final BeanProperty draftDirty;
@@ -462,6 +464,7 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
this.softDelete = (softDeleteProperty != null);
this.idProperty = listHelper.getId();
this.versionProperty = listHelper.getVersionProperty();
+ this.unmappedJson = listHelper.getUnmappedJson();
this.draft = listHelper.getDraft();
this.draftDirty = listHelper.getDraftDirty();
this.propMap = listHelper.getPropertyMap();
@@ -474,7 +477,6 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
this.propertiesMutable = listHelper.getMutable();
this.unidirectional = listHelper.getUnidirectional();
this.propertiesOne = listHelper.getOnes();
- //this.propertiesOneExported = listHelper.getOneExported();
this.propertiesOneExportedSave = listHelper.getOneExportedSave();
this.propertiesOneExportedDelete = listHelper.getOneExportedDelete();
this.propertiesOneImported = listHelper.getOneImported();
@@ -2468,6 +2470,12 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
return draftableElement;
}
+ public void setUnmappedJson(EntityBean bean, Map unmappedProperties) {
+ if( unmappedJson != null) {
+ unmappedJson.setValueIntercept(bean, unmappedProperties);
+ }
+ }
+
/**
* Set the draft to true for this entity bean instance.
* This bean is being loaded via asDraft() query.
@@ -2597,6 +2605,13 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType {
return propMap.values();
}
+ /**
+ * Return the property that holds unmapped JSON content.
+ */
+ public BeanProperty propertyUnmappedJson() {
+ return unmappedJson;
+ }
+
/**
* Return the non transient non id properties.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java
index 6dbf2e357..bdc79731d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorJsonHelp.java
@@ -159,6 +159,9 @@ public class BeanDescriptorJsonHelp {
} while (true);
+ if (unmappedProperties != null) {
+ desc.setUnmappedJson(bean, unmappedProperties);
+ }
Object contextBean = null;
Object id = desc.beanId(bean);
if (id != null) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
index 780c574e9..fa8b70bc5 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java
@@ -242,8 +242,8 @@ public class BeanProperty implements ElPropertyValue, Property {
int deployOrder;
final boolean jsonSerialize;
-
final boolean jsonDeserialize;
+ final boolean unmappedJson;
final boolean draft;
@@ -279,6 +279,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.dbInsertable = deploy.isDbInsertable();
this.dbUpdatable = deploy.isDbUpdateable();
this.excludedFromHistory = deploy.isExcludedFromHistory();
+ this.unmappedJson = deploy.isUnmappedJson();
this.draft = deploy.isDraft();
this.draftDirty = deploy.isDraftDirty();
this.draftOnly = deploy.isDraftOnly();
@@ -426,6 +427,7 @@ public class BeanProperty implements ElPropertyValue, Property {
this.propertyType = source.getPropertyType();
this.field = source.getField();
this.docOptions = source.docOptions;
+ this.unmappedJson = source.unmappedJson;
this.elPrefix = override.replace(source.elPrefix, source.dbColumn);
this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn);
@@ -1201,6 +1203,13 @@ public class BeanProperty implements ElPropertyValue, Property {
return !excludedFromHistory && descriptor.isHistorySupport();
}
+ /**
+ * Return true if this property hold unmapped JSON.
+ */
+ public boolean isUnmappedJson() {
+ return unmappedJson;
+ }
+
/**
* Return true if this property only exists on the draft table.
*/
@@ -1302,6 +1311,10 @@ public class BeanProperty implements ElPropertyValue, Property {
}
}
+ public boolean isJsonSerialize() {
+ return jsonSerialize;
+ }
+
@SuppressWarnings(value = "unchecked")
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
index a0aa3f45e..9cb0532c0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
@@ -203,6 +203,7 @@ public class DeployBeanProperty {
private boolean draftReset;
private boolean softDelete;
+ private boolean unmappedJson;
private String dbComment;
@@ -939,6 +940,15 @@ public class DeployBeanProperty {
return softDelete;
}
+ public void setUnmappedJson() {
+ this.unmappedJson = true;
+ this.isTransient = true;
+ }
+
+ public boolean isUnmappedJson() {
+ return unmappedJson;
+ }
+
public void setDbComment(String dbComment) {
this.dbComment = dbComment;
}
@@ -966,4 +976,5 @@ public class DeployBeanProperty {
public String getDbColumnDefault() {
return dbColumnDefault;
}
+
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
index 3c055d507..cdee51e94 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
@@ -27,6 +27,8 @@ public class DeployBeanPropertyLists {
private BeanProperty versionProperty;
+ private BeanProperty unmappedJson;
+
private BeanProperty draft;
private BeanProperty draftDirty;
@@ -167,6 +169,9 @@ public class DeployBeanPropertyLists {
if (prop.isDraft()) {
draft = prop;
}
+ if (prop.isUnmappedJson()) {
+ unmappedJson = prop;
+ }
return;
}
if (prop.isId()) {
@@ -205,8 +210,7 @@ public class DeployBeanPropertyLists {
if (versionProperty == null) {
versionProperty = prop;
} else {
- logger.warn("Multiple @Version properties - property " + prop.getFullBeanName()
- + " not treated as a version property");
+ logger.warn("Multiple @Version properties - property " + prop.getFullBeanName() + " not treated as a version property");
}
} else if (prop.isDraftDirty()) {
draftDirty = prop;
@@ -326,6 +330,10 @@ public class DeployBeanPropertyLists {
return draftDirty;
}
+ public BeanProperty getUnmappedJson() {
+ return unmappedJson;
+ }
+
public BeanProperty getDraft() {
return draft;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
index edfdac0a4..92334ace4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java
@@ -13,9 +13,6 @@ import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
-import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound;
-import com.avaje.ebeaninternal.server.lib.util.StringHelper;
-import com.avaje.ebeaninternal.server.type.CtCompoundType;
import com.avaje.ebeaninternal.server.type.DataEncryptSupport;
import com.avaje.ebeaninternal.server.type.ScalarType;
import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase;
@@ -26,7 +23,6 @@ import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.sql.Types;
-import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -364,6 +360,9 @@ public class AnnotationFields extends AnnotationParser {
prop.setJsonSerialize(jsonIgnore.serialize());
prop.setJsonDeserialize(jsonIgnore.deserialize());
}
+ if (get(prop, UnmappedJson.class) != null) {
+ prop.setUnmappedJson();
+ }
}
private boolean hasRelationshipItem(DeployBeanProperty prop) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java
index f7713cd4c..8f69d4b60 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJson.java
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.text.json;
import com.avaje.ebean.FetchPath;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.config.JsonConfig;
+import com.avaje.ebean.text.json.EJson;
import com.avaje.ebean.text.json.JsonIOException;
import com.avaje.ebean.text.json.JsonWriteBeanVisitor;
import com.avaje.ebean.text.json.JsonWriter;
@@ -465,7 +466,9 @@ public class WriteJson implements JsonWriter {
}
private boolean isIncludeTransientProperty(BeanProperty prop) {
- if (!explicitAllProps && currentIncludeProps != null) {
+ if (prop.isUnmappedJson()) {
+ return false;
+ } else if (!explicitAllProps && currentIncludeProps != null) {
// explicitly controlled by pathProperties
return currentIncludeProps.contains(prop.getName());
} else {
@@ -501,6 +504,18 @@ public class WriteJson implements JsonWriter {
}
}
+ BeanProperty unmappedJson = desc.propertyUnmappedJson();
+ if (unmappedJson != null && unmappedJson.isJsonSerialize()) {
+ Map map = (Map)unmappedJson.getValue(currentBean);
+ if (map != null) {
+ // write to JSON at the current level
+ for (Map.Entry entry : map.entrySet()) {
+ writeJson.writeFieldName(entry.getKey());
+ EJson.write(entry.getValue(), writeJson.generator);
+ }
+ }
+ }
+
if (visitor != null) {
visitor.visit(currentBean, writeJson);
}
diff --git a/src/test/java/com/avaje/tests/json/TestUnmappedProperties.java b/src/test/java/com/avaje/tests/json/TestUnmappedProperties.java
new file mode 100644
index 000000000..86a9f53ce
--- /dev/null
+++ b/src/test/java/com/avaje/tests/json/TestUnmappedProperties.java
@@ -0,0 +1,54 @@
+package com.avaje.tests.json;
+
+import com.avaje.ebean.BaseTestCase;
+import com.avaje.ebean.Ebean;
+import com.avaje.tests.model.json.EBasicJsonUnmapped;
+import org.junit.Test;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class TestUnmappedProperties extends BaseTestCase {
+
+ @Test
+ public void toJson() {
+
+ Map nested = new LinkedHashMap<>();
+ nested.put("alpha", "aa");
+ nested.put("beta", "bb");
+
+
+ Map unmapped = new LinkedHashMap<>();
+ unmapped.put("one", 42);
+ unmapped.put("nested", nested);
+
+ EBasicJsonUnmapped bean = new EBasicJsonUnmapped();
+ bean.setName("someName");
+ bean.setUnmapped(unmapped);
+
+
+ String asJson = Ebean.json().toJson(bean);
+
+ assertThat(asJson).contains("{\"name\":\"someName\",\"one\":42,\"nested\":{\"alpha\":\"aa\",\"beta\":\"bb\"}}");
+
+ }
+
+ @Test
+ public void fromJson() {
+
+ String json = "{\"name\":\"someName\",\"one\":42,\"nested\":{\"alpha\":\"aa\",\"beta\":\"bb\"}}";
+
+ EBasicJsonUnmapped bean = Ebean.json().toBean(EBasicJsonUnmapped.class, json);
+
+ assertThat(bean.getName()).isEqualTo("someName");
+ assertThat(bean.getUnmapped().get("one")).isEqualTo(42L);
+
+ @SuppressWarnings("unchecked")
+ Map fromNested = (Map)bean.getUnmapped().get("nested");
+ assertThat(fromNested).containsKeys("alpha", "beta");
+ assertThat(fromNested).containsValues("aa", "bb");
+ }
+
+}
diff --git a/src/test/java/com/avaje/tests/model/json/EBasicJsonUnmapped.java b/src/test/java/com/avaje/tests/model/json/EBasicJsonUnmapped.java
new file mode 100644
index 000000000..7e2f0e411
--- /dev/null
+++ b/src/test/java/com/avaje/tests/model/json/EBasicJsonUnmapped.java
@@ -0,0 +1,55 @@
+package com.avaje.tests.model.json;
+
+import com.avaje.ebean.annotation.UnmappedJson;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Version;
+import java.util.Map;
+
+@Entity
+public class EBasicJsonUnmapped {
+
+ @Id
+ Long id;
+
+ @Version
+ Long version;
+
+ String name;
+
+ @UnmappedJson
+ Map unmapped;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map getUnmapped() {
+ return unmapped;
+ }
+
+ public void setUnmapped(Map unmapped) {
+ this.unmapped = unmapped;
+ }
+}