![JAR search and dependency download from the Maven repository](/logo.png)
com.viaoa.json.jackson.OAJacksonSerializer Maven / Gradle / Ivy
Show all versions of oa-core Show documentation
package com.viaoa.json.jackson;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.viaoa.hub.Hub;
import com.viaoa.json.OAJson;
import com.viaoa.json.OAJson.StackItem;
import com.viaoa.object.OALinkInfo;
import com.viaoa.object.OAObject;
import com.viaoa.object.OAObjectInfo;
import com.viaoa.object.OAObjectInfoDelegate;
import com.viaoa.object.OAObjectKey;
import com.viaoa.object.OAObjectPropertyDelegate;
import com.viaoa.object.OAObjectReflectDelegate;
import com.viaoa.object.OAPropertyInfo;
import com.viaoa.object.OAThreadLocalDelegate;
import com.viaoa.pojo.Pojo;
import com.viaoa.pojo.PojoImportMatch;
import com.viaoa.pojo.PojoLink;
import com.viaoa.pojo.PojoLinkFkey;
import com.viaoa.pojo.PojoLinkOne;
import com.viaoa.pojo.PojoLinkOneReference;
import com.viaoa.pojo.PojoLinkUnique;
import com.viaoa.pojo.PojoProperty;
import com.viaoa.util.OAConv;
import com.viaoa.util.OAConverter;
import com.viaoa.util.OADate;
import com.viaoa.util.OADateTime;
import com.viaoa.util.OAPropertyPath;
import com.viaoa.util.OAString;
import com.viaoa.util.OATime;
//qqqq needs to use "compoundKey"
// qqqqqq concat string, separated by '-'
/**
* Used by OAJson to convert OAObject(s) & Hub to JSON. Includes mapping to work with POJO classes.
*
*/
public class OAJacksonSerializer extends JsonSerializer {
@Override
public void serialize(final OAObject value, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
final OAJson oaj = OAThreadLocalDelegate.getOAJackson();
final OAObject oaObj = (OAObject) value;
final OAObjectInfo oi = OAObjectInfoDelegate.getOAObjectInfo(oaObj.getClass());
gen.writeStartObject();
boolean b = (oaj.getStackItem() == null);
if (b) {
StackItem stackItem = new StackItem();
stackItem.parent = null;
stackItem.oi = oi;
stackItem.li = null;
stackItem.obj = value;
oaj.setStackItem(stackItem);
}
try {
_serialize(oaj, oaObj, oi, value, gen, serializers);
} finally {
if (b) {
oaj.setStackItem(null);
}
}
}
protected void _serialize(final OAJson oaj, final OAObject oaObj, final OAObjectInfo oi, final OAObject value, final JsonGenerator gen,
final SerializerProvider serializers) throws IOException {
//qqqqqq if writeAsPojo, then this needs to us OAObjectInfo.pojo to determine Id properties
//qqq if more than one, use compoundKey as string with '-'
// write id props
boolean bNullId = true;
for (OAPropertyInfo pi : oi.getPropertyInfos()) {
if (!pi.getId()) {
continue;
}
if (oaj.getWriteAsPojo() && pi.getAutoAssign()) {
if (OAString.isNotEmpty(oi.getImportMatchPropertyNames())) {
boolean b = false;
for (String pp : oi.getImportMatchPropertyPaths()) {
OAPropertyPath ppx = new OAPropertyPath(oi.getForClass(), pp);
if (pp.indexOf('.') < 0 && pp.equalsIgnoreCase(pi.getName())) {
b = true;
break;
}
}
if (!b) {
continue;
}
}
}
String propertyName = pi.getLowerName();
Object objx = pi.getValue(oaObj);
if (!oaj.getUsePropertyCallback(oaObj, propertyName)) {
continue;
}
propertyName = oaj.getPropertyNameCallback(oaObj, propertyName);
objx = oaj.getPropertyValueCallback(oaObj, propertyName, objx);
if (objx == null) {
gen.writeNullField(pi.getLowerName());
} else {
writeProperty(pi, gen, oaObj);
bNullId = false;
}
}
if (bNullId && !oaj.getWriteAsPojo()) {
gen.writeNumberField("guid", oaObj.getGuid());
}
// write (non-id) props
for (OAPropertyInfo pi : oi.getPropertyInfos()) {
if (pi.getId()) {
continue;
}
if (pi.getIsFkeyOnly() && oaj.getWriteAsPojo()) {
continue;
}
if (pi.getEnumPropertyName() != null) {
if (pi.getName().endsWith("String")) continue;
}
writeProperty(pi, gen, oaObj);
}
if (oaj.getWriteAsPojo()) {
Pojo pojo = oi.getPojo();
writeExtraPojoProperties(oaj, oi, oaObj, gen);
}
final ArrayList alPropertyPaths = oaj == null ? null : oaj.getPropertyPaths();
final boolean bIncludeOwned = oaj == null ? true : oaj.getIncludeOwned();
// write one references
for (OALinkInfo li : oi.getLinkInfos()) {
if (li.getType() != li.TYPE_ONE) {
continue;
}
if (li.getPrivateMethod()) {
continue;
}
if (li.getCalculated()) {
continue;
}
/*
if (oaj.getWriteAsPojo()) {
//qqqq make this dynamic and use propertyPaths to include
if (!li.getOwner()) {
continue;
}
}
*/
String propertyName = li.getLowerName();
if (!oaj.getUsePropertyCallback(oaObj, propertyName)) {
continue;
}
boolean bSerialized = false;
if ((oaj != null && oaj.getIncludeAll()) || shouldInclude(oaj, li, bIncludeOwned, alPropertyPaths)) {
propertyName = oaj.getPropertyNameCallback(oaObj, propertyName);
StackItem si = new StackItem();
si.parent = oaj.getStackItem();
si.li = li;
si.obj = oaObj;
oaj.setStackItem(si);
try {
OAObject objx = (OAObject) li.getValue(oaObj);
objx = (OAObject) oaj.getPropertyValueCallback(oaObj, propertyName, objx);
if (objx == null) {
gen.writeNullField(propertyName);
bSerialized = true;
} else {
if (oaj != null && !oaj.getCascade().wasCascaded(objx, true)) {
bSerialized = true;
gen.writeObjectField(li.getLowerName(), objx);
} else {
bSerialized = false;
}
}
} finally {
oaj.setStackItem(si.parent);
}
}
if (!bSerialized) {
OAObjectKey key = null;
Object obj = OAObjectPropertyDelegate.getProperty(oaObj, li.getName(), false, true);
obj = oaj.getPropertyValueCallback(oaObj, li.getLowerName(), obj);
if (obj instanceof OAObject) {
key = ((OAObject) obj).getObjectKey();
} else if (obj instanceof OAObjectKey) {
key = (OAObjectKey) obj;
}
if (key == null) {
gen.writeNullField(propertyName);
} else {
String id = OAJson.convertObjectKeyToJsonSinglePartId(key);
if (id.indexOf('-') >= 0 || id.indexOf("guid.") == 0) {
gen.writeStringField(li.getLowerName(), id);
} else {
if (OAString.isNumber(id)) {
gen.writeNumberField(li.getLowerName(), OAConv.toLong(id));
} else {
gen.writeStringField(li.getLowerName(), id);
}
}
}
}
}
// write many references
for (OALinkInfo li : oi.getLinkInfos()) {
if (li.getType() != li.TYPE_MANY) {
continue;
}
if (li.getPrivateMethod()) {
continue;
}
if (li.getCalculated()) {
continue;
}
String propertyName = li.getLowerName();
if (!oaj.getUsePropertyCallback(oaObj, propertyName)) {
continue;
}
if (oaj.getWriteAsPojo()) {
if (!li.getOwner()) {
continue;
}
}
// only send owned objects for the root object(s)
// also include any owned auto-created linkOne owned links
boolean bx = bIncludeOwned;
if (bx) {
OAJson.StackItem si = oaj.getStackItem();
bx = si == null || si.parent == null;
//qqqqqqq create a rule & unit test for this
if (!bx && si.li.isOne2One() && si.li.getOwner() && si.li.getAutoCreateNew()) {
bx = true;
}
}
if ((oaj != null && oaj.getIncludeAll()) || shouldInclude(oaj, li, bx, alPropertyPaths)) {
StackItem si = new StackItem();
si.parent = oaj.getStackItem();
si.li = li;
oaj.setStackItem(si);
try {
Hub hub = (Hub) li.getValue(oaObj);
gen.writeArrayFieldStart(propertyName);
for (OAObject objx : (Hub) li.getValue(oaObj)) {
si.obj = objx;
// check cascade to see if its been sent ...if so, then only output key (/guid)
// note: deserializer needs to check array values for object, string, number to "know" how to get it
if (oaj != null && !oaj.getCascade().wasCascaded(objx, true)) {
gen.writeObject(objx);
} else {
OAObjectKey key = objx.getObjectKey();
String id = OAJson.convertObjectKeyToJsonSinglePartId(key);
if (id.indexOf('-') >= 0 || id.indexOf("guid.") == 0) {
gen.writeString(id);
} else {
gen.writeNumber(OAConv.toLong(id));
}
}
}
} finally {
oaj.setStackItem(si.parent);
gen.writeEndArray();
}
} else {
// if hub is loaded and it is empty, then send empty array (for convenience only)
Object obj = OAObjectPropertyDelegate.getProperty(oaObj, li.getName(), false, true);
if (obj instanceof Hub) {
if (((Hub) obj).isEmpty()) {
gen.writeArrayFieldStart(li.getLowerName());
gen.writeEndArray();
}
}
}
}
// todo: want to add any calcs ??
gen.writeEndObject();
}
/**
* Include other properties that could be needed for Pojo. Pojo classes dont always have a pkey property, but uniqueness can be found
* using other data. Uses pojo information to determine other fkey'like values to include. This includes importMatch, links with unique
* property, and fkeys from another link.
*
* see OABuilder model OABuilderPojo
*/
protected void writeExtraPojoProperties(final OAJson oaj, final OAObjectInfo oi, final OAObject oaObj, final JsonGenerator gen)
throws IOException {
Pojo pojo = oi.getPojo();
for (PojoLink pl : pojo.getPojoLinks()) {
PojoLinkOne plo = pl.getPojoLinkOne();
if (plo != null) {
writePojoLinkOne(oaj, oi, oaObj, gen, plo);
}
}
}
protected void writePojoLinkOne(final OAJson oaj, final OAObjectInfo oi, final OAObject oaObj, final JsonGenerator gen,
final PojoLinkOne plo) throws IOException {
// fkeys
for (PojoLinkFkey plf : plo.getPojoLinkFkeys()) {
PojoProperty pjp = plf.getPojoProperty();
writePojoProperty(oaj, oi, oaObj, gen, pjp);
}
// importMatches
for (PojoImportMatch pim : plo.getPojoImportMatches()) {
PojoProperty pjp = pim.getPojoProperty();
if (pim != null) {
writePojoProperty(oaj, oi, oaObj, gen, pjp);
}
PojoLinkOneReference plor = pim.getPojoLinkOneReference();
if (plor == null) {
continue;
}
PojoLinkOne plox = plor.getPojoLinkOne();
writePojoLinkOne(oaj, oi, oaObj, gen, plo);
}
// link with unique property
PojoLinkUnique plu = plo.getPojoLinkUnique();
if (plu != null) {
PojoProperty pjp = plu.getPojoProperty();
if (pjp != null) {
writePojoProperty(oaj, oi, oaObj, gen, pjp);
}
PojoLinkOneReference plor = plu.getPojoLinkOneReference();
if (plor != null) {
PojoLinkOne plox = plor.getPojoLinkOne();
writePojoLinkOne(oaj, oi, oaObj, gen, plox);
}
}
}
protected void writePojoProperty(final OAJson oaj, final OAObjectInfo oi, final OAObject oaObj, final JsonGenerator gen,
final PojoProperty pjp) throws IOException {
String propertyName = pjp.getName();
String pp = pjp.getPropertyPath();
OAPropertyPath ppx = new OAPropertyPath(oi.getForClass(), pp);
OAPropertyInfo pi = ppx.getEndPropertyInfo();
Object objx = oaObj.getProperty(pp);
propertyName = oaj.getPropertyNameCallback(oaObj, propertyName);
objx = oaj.getPropertyValueCallback(oaObj, propertyName, objx);
if (objx == null) {
gen.writeNullField(propertyName);
} else {
writeProperty(pi, propertyName, objx, gen, oaObj);
}
}
protected boolean shouldInclude(OAJson oaj, OALinkInfo li, boolean bIncludeOwned, ArrayList alPropertyPaths) {
if (li == null) {
return false;
}
if (bIncludeOwned && (li.getOwner() || li.getAutoCreateNew())) {
return true;
}
if (alPropertyPaths == null) {
return false;
}
String cpp = oaj.getCurrentPropertyPath();
cpp = OAString.append(cpp, li.getName(), ".");
cpp = cpp.toLowerCase();
for (String pp : alPropertyPaths) {
if (pp.toLowerCase().indexOf(cpp) == 0) {
return true;
}
}
return false;
}
protected void writeProperty(OAPropertyInfo pi, JsonGenerator gen, OAObject oaObj) throws IOException {
writeProperty(pi, pi.getLowerName(), null, gen, oaObj);
}
protected void writeProperty(OAPropertyInfo pi, final String lowerName, Object value, JsonGenerator gen, OAObject oaObj)
throws IOException {
boolean bCheckValue = (value == null);
if (bCheckValue) {
value = pi.getValue(oaObj);
if (pi.getIsPrimitive() && pi.getTrackPrimitiveNull() && OAObjectReflectDelegate.getPrimitiveNull(oaObj, lowerName)) {
value = null;
}
}
if (value == null) {
gen.writeNullField(lowerName);
return;
}
if (value != null && !(value instanceof String) && OAConverter.getConverter(value.getClass()) == null) {
gen.writeNullField(lowerName);
return;
}
/* there are now prop*Enum and prop*String properties that will be a String
if (pi.isNameValue() && (value instanceof Integer)) {
value = (String) pi.getNameValues().get((Integer) value);
}
*/
if (value instanceof String) {
if (pi.isJson()) {
gen.writeFieldName(lowerName);
gen.writeRawValue((String) value);
} else {
gen.writeStringField(lowerName, (String) value);
}
} else if (value instanceof Boolean) {
gen.writeBooleanField(lowerName, (boolean) value);
} else if (value instanceof BigDecimal) {
gen.writeNumberField(lowerName, (BigDecimal) value);
} else if (value instanceof Double) {
BigDecimal bd = OAConv.toBigDecimal((Double) value, pi.getDecimalPlaces());
gen.writeNumberField(lowerName, bd);
} else if (value instanceof Float) {
BigDecimal bd = OAConv.toBigDecimal((Float) value, pi.getDecimalPlaces());
gen.writeNumberField(lowerName, bd);
} else if (value instanceof Long) {
gen.writeNumberField(lowerName, (Long) value);
} else if (value instanceof Integer) {
gen.writeNumberField(lowerName, (Integer) value);
} else if (value instanceof Short) {
gen.writeNumberField(lowerName, (Short) value);
} else if (value instanceof OADate) {
String fmt = pi.getFormat();
if (OAString.isEmpty(fmt)) {
fmt = "yyyy-MM-dd";
}
String result = ((OADate) value).toString(fmt);
gen.writeStringField(lowerName, result);
} else if (value instanceof OATime) {
String fmt = pi.getFormat();
if (OAString.isEmpty(fmt)) {
fmt = "HH:mm:ss";
}
String result = ((OATime) value).toString(fmt);
gen.writeStringField(lowerName, result);
} else if (value instanceof OADateTime) {
String fmt = pi.getFormat();
if (OAString.isEmpty(fmt)) {
fmt = "yyyy-MM-dd'T'HH:mm:ss";
}
String result = ((OADateTime) value).toString(fmt); // "2020-12-26T19:21:09"
gen.writeStringField(lowerName, result);
} else if (value instanceof byte[]) {
gen.writeBinaryField(lowerName, (byte[]) value);
} else {
String result = OAConv.toString(value);
gen.writeStringField(lowerName, result);
}
}
}