IDEMPIERE-2981 - Implement JSON field type (#2255)
* IDEMPIERE-2981 - Implement JSON Field Type * IDEMPIERE-2981 - Added field to the Test Window and Unit Tests * IDEMPIERE-2981 - Fixed migration scripts * IDEMPIERE-2981 - Changed oracle json syntax * IDEMPIERE-2981 - Increased the number of lines of jsonData test field * IDEMPIERE-2981 - Validate and Prettify JSON string on the field editor * IDEMPIERE-2981 - Support for oracle * IDEMPIERE-2981 - Applied patch from Carlos Ruiz
This commit is contained in:
parent
a45283b855
commit
5f44c9c19d
|
@ -0,0 +1,10 @@
|
|||
-- IDEMPIERE-2981 - Implement JSON Field type
|
||||
SELECT register_migration_script('202402261300_IDEMPIERE-2981.sql') FROM dual;
|
||||
|
||||
SET SQLBLANKLINES ON
|
||||
SET DEFINE OFF
|
||||
|
||||
-- Feb 26, 2024, 1:00:29 PM CET
|
||||
INSERT INTO AD_Reference (AD_Reference_ID,Name,Description,ValidationType,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,EntityType,IsOrderByValue,AD_Reference_UU,ShowInactive) VALUES (200267,'JSON','JSON format values','D',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,'D','N','b6fcc751-edd8-4421-acd0-3cde02a9576d','N')
|
||||
;
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
-- IDEMPIERE-2981 - Implement JSON Field type
|
||||
SELECT register_migration_script('202402261354_IDEMPIERE-2981.sql') FROM dual;
|
||||
|
||||
SET SQLBLANKLINES ON
|
||||
SET DEFINE OFF
|
||||
|
||||
-- Feb 26, 2024, 1:54:35 PM CET
|
||||
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,PrintName,EntityType,AD_Element_UU) VALUES (203924,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,'JsonData','JSON Data','The json field stores json data.','JSON Data','D','c4ea7a81-96a9-4a5d-bb87-e913e1c8ed48')
|
||||
;
|
||||
|
||||
-- Feb 26, 2024, 1:55:37 PM CET
|
||||
INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml,IsPartitionKey) VALUES (216570,0,'JSON Data','The json field stores json data.',135,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','927b83df-d161-4332-ad44-8ffed99e8cf4','Y',0,'N','N','N','N')
|
||||
;
|
||||
|
||||
-- Feb 28, 2024, 5:41:55 PM CET
|
||||
ALTER TABLE Test ADD JsonData CLOB DEFAULT NULL CONSTRAINT test_jsondata_ij CHECK (JsonData IS JSON)
|
||||
;
|
||||
|
||||
-- Feb 26, 2024, 1:56:08 PM CET
|
||||
INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (208472,'JSON Data','The json field stores json data.',152,216570,'Y',100,310,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','e47ef529-71ba-4f9b-9014-b188e17e8ef4','Y',290,5)
|
||||
;
|
||||
|
||||
-- Feb 29, 2024, 1:52:50 PM CET
|
||||
UPDATE AD_Field SET NumLines=5,Updated=TO_TIMESTAMP('2024-02-29 13:52:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208472
|
||||
;
|
||||
|
||||
-- Feb 29, 2024, 2:07:30 PM CET
|
||||
INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','Invalid JSON',0,0,'Y',TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,200876,'InvalidJSON','D','a263376f-a12e-4943-92f1-7d7ce8a67a2b')
|
||||
;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
-- IDEMPIERE-2981 - Implement JSON Field type
|
||||
SELECT register_migration_script('202402261300_IDEMPIERE-2981.sql') FROM dual;
|
||||
|
||||
-- Feb 26, 2024, 1:00:29 PM CET
|
||||
INSERT INTO AD_Reference (AD_Reference_ID,Name,Description,ValidationType,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,EntityType,IsOrderByValue,AD_Reference_UU,ShowInactive) VALUES (200267,'JSON','JSON format values','D',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,'D','N','b6fcc751-edd8-4421-acd0-3cde02a9576d','N')
|
||||
;
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
-- IDEMPIERE-2981 - Implement JSON Field type
|
||||
SELECT register_migration_script('202402261354_IDEMPIERE-2981.sql') FROM dual;
|
||||
|
||||
-- Feb 26, 2024, 1:54:35 PM CET
|
||||
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,PrintName,EntityType,AD_Element_UU) VALUES (203924,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,'JsonData','JSON Data','The json field stores json data.','JSON Data','D','c4ea7a81-96a9-4a5d-bb87-e913e1c8ed48')
|
||||
;
|
||||
|
||||
-- Feb 26, 2024, 1:55:37 PM CET
|
||||
INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml,IsPartitionKey) VALUES (216570,0,'JSON Data','The json field stores json data.',135,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','927b83df-d161-4332-ad44-8ffed99e8cf4','Y',0,'N','N','N','N')
|
||||
;
|
||||
|
||||
-- Feb 26, 2024, 1:55:55 PM CET
|
||||
ALTER TABLE Test ADD COLUMN JsonData JSON DEFAULT NULL
|
||||
;
|
||||
|
||||
-- Feb 26, 2024, 1:56:08 PM CET
|
||||
INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (208472,'JSON Data','The json field stores json data.',152,216570,'Y',100,310,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','e47ef529-71ba-4f9b-9014-b188e17e8ef4','Y',290,5)
|
||||
;
|
||||
|
||||
-- Feb 29, 2024, 1:52:50 PM CET
|
||||
UPDATE AD_Field SET NumLines=5,Updated=TO_TIMESTAMP('2024-02-29 13:52:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208472
|
||||
;
|
||||
|
||||
-- Feb 29, 2024, 2:07:30 PM CET
|
||||
INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','Invalid JSON',0,0,'Y',TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,200876,'InvalidJSON','D','a263376f-a12e-4943-92f1-7d7ce8a67a2b')
|
||||
;
|
||||
|
|
@ -167,7 +167,7 @@ public class TabCreateFields extends SvrProcess
|
|||
}
|
||||
if (column.getAD_Reference_ID() == DisplayType.Text) {
|
||||
field.setNumLines(3);
|
||||
} else if (column.getAD_Reference_ID() == DisplayType.TextLong) {
|
||||
} else if (column.getAD_Reference_ID() == DisplayType.TextLong || column.getAD_Reference_ID() == DisplayType.JSON) {
|
||||
field.setNumLines(5);
|
||||
} else if (column.getAD_Reference_ID() == DisplayType.Memo) {
|
||||
field.setNumLines(8);
|
||||
|
|
|
@ -1421,7 +1421,7 @@ public class GridTabCSVImporter implements IGridTabImporter
|
|||
return (new Optional(new ParseBigDecimal(new DecimalFormatSymbols(Language.getLoginLanguage().getLocale()))));
|
||||
} else if (DisplayType.YesNo == field.getDisplayType()) {
|
||||
return (new Optional(new ParseBool("y", "n")));
|
||||
} else if (DisplayType.TextLong == field.getDisplayType()) {
|
||||
} else if (DisplayType.TextLong == field.getDisplayType() || DisplayType.JSON == field.getDisplayType()) {
|
||||
return (new Optional(new StrMinMax(1, Long.MAX_VALUE)));
|
||||
} else if (DisplayType.isText(field.getDisplayType())) {
|
||||
return (new Optional(new StrMinMax(1, field.getFieldLength())));
|
||||
|
|
|
@ -202,7 +202,18 @@ public interface AdempiereDatabase
|
|||
*/
|
||||
public String TO_NUMBER (BigDecimal number, int displayType);
|
||||
|
||||
/**
|
||||
* Return string as JSON object for INSERT statements
|
||||
* @param value
|
||||
* @return value as JSON
|
||||
*/
|
||||
public String TO_JSON (String value);
|
||||
|
||||
/**
|
||||
* @return string with right casting for JSON inserts
|
||||
*/
|
||||
public String getJSONCast ();
|
||||
|
||||
/**
|
||||
* Get next sequence number in this Sequence
|
||||
* @param Name Sequence name
|
||||
|
@ -424,6 +435,11 @@ public interface AdempiereDatabase
|
|||
*/
|
||||
public String getClobDataType();
|
||||
|
||||
/**
|
||||
* @return json object data type name
|
||||
*/
|
||||
public String getJsonDataType();
|
||||
|
||||
/**
|
||||
* @return time stamp data type name
|
||||
*/
|
||||
|
|
|
@ -2106,6 +2106,7 @@ public class GridField
|
|||
if (m_vo.displayType == DisplayType.Text
|
||||
|| m_vo.displayType == DisplayType.Memo
|
||||
|| m_vo.displayType == DisplayType.TextLong
|
||||
|| m_vo.displayType == DisplayType.JSON
|
||||
|| m_vo.displayType == DisplayType.Binary
|
||||
|| m_vo.displayType == DisplayType.RowID
|
||||
|| isEncrypted())
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.compiere.util.KeyNamePair;
|
|||
|
||||
/** Generated Interface for Test
|
||||
* @author iDempiere (generated)
|
||||
* @version Release 11
|
||||
* @version Release 12
|
||||
*/
|
||||
public interface I_Test
|
||||
{
|
||||
|
@ -55,8 +55,8 @@ public interface I_Test
|
|||
/** Column name AD_Client_ID */
|
||||
public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID";
|
||||
|
||||
/** Get Tenant.
|
||||
* Tenant for this installation.
|
||||
/** Get Client.
|
||||
* Client/Tenant for this installation.
|
||||
*/
|
||||
public int getAD_Client_ID();
|
||||
|
||||
|
@ -64,12 +64,12 @@ public interface I_Test
|
|||
public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID";
|
||||
|
||||
/** Set Organization.
|
||||
* Organizational entity within tenant
|
||||
* Organizational entity within client
|
||||
*/
|
||||
public void setAD_Org_ID (int AD_Org_ID);
|
||||
|
||||
/** Get Organization.
|
||||
* Organizational entity within tenant
|
||||
* Organizational entity within client
|
||||
*/
|
||||
public int getAD_Org_ID();
|
||||
|
||||
|
@ -253,6 +253,19 @@ public interface I_Test
|
|||
*/
|
||||
public boolean isActive();
|
||||
|
||||
/** Column name JsonData */
|
||||
public static final String COLUMNNAME_JsonData = "JsonData";
|
||||
|
||||
/** Set JSON Data.
|
||||
* The json field stores json data.
|
||||
*/
|
||||
public void setJsonData (Object JsonData);
|
||||
|
||||
/** Get JSON Data.
|
||||
* The json field stores json data.
|
||||
*/
|
||||
public Object getJsonData();
|
||||
|
||||
/** Column name M_Locator_ID */
|
||||
public static final String COLUMNNAME_M_Locator_ID = "M_Locator_ID";
|
||||
|
||||
|
|
|
@ -380,7 +380,7 @@ public class MColumn extends X_AD_Column implements ImmutablePOSupport
|
|||
}
|
||||
|
||||
int displayType = getAD_Reference_ID();
|
||||
if (DisplayType.isLOB(displayType)) // LOBs are 0
|
||||
if (DisplayType.isLOB(displayType) || displayType == DisplayType.JSON) // LOBs are 0
|
||||
{
|
||||
if (getFieldLength() != 0)
|
||||
setFieldLength(0);
|
||||
|
|
|
@ -115,7 +115,7 @@ public abstract class PO
|
|||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -7758079724744033518L;
|
||||
private static final long serialVersionUID = 6591172659109078284L;
|
||||
|
||||
/* String key to create a new record based in UUID constructor */
|
||||
public static final String UUID_NEW_RECORD = "";
|
||||
|
@ -3046,6 +3046,8 @@ public abstract class PO
|
|||
{
|
||||
if (value instanceof Timestamp && dt == DisplayType.Date)
|
||||
sql.append("trunc(cast(? as date))");
|
||||
else if (dt == DisplayType.JSON)
|
||||
sql.append(DB.getJSONCast());
|
||||
else
|
||||
sql.append("?");
|
||||
|
||||
|
@ -3071,7 +3073,7 @@ public abstract class PO
|
|||
} else {
|
||||
params.add(encrypt(i,value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
params.add(value);
|
||||
|
@ -3660,6 +3662,8 @@ public abstract class PO
|
|||
{
|
||||
if (value instanceof Timestamp && dt == DisplayType.Date)
|
||||
sqlValues.append("trunc(cast(? as date))");
|
||||
else if (dt == DisplayType.JSON)
|
||||
sqlValues.append(DB.getJSONCast());
|
||||
else
|
||||
sqlValues.append("?");
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ public class PO_LOB implements Serializable
|
|||
try
|
||||
{
|
||||
pstmt = con.prepareStatement(sql.toString());
|
||||
if (m_displayType == DisplayType.TextLong)
|
||||
if (m_displayType == DisplayType.TextLong || m_displayType == DisplayType.JSON)
|
||||
pstmt.setString(1, (String)m_value);
|
||||
else
|
||||
pstmt.setBytes(1, (byte[])m_value);
|
||||
|
|
|
@ -164,6 +164,7 @@ public class SystemIDs
|
|||
public final static int REFERENCE_DATATYPE_TABLEDIR_UU = 200234;
|
||||
public final static int REFERENCE_DATATYPE_TEXT = 14;
|
||||
public final static int REFERENCE_DATATYPE_TEXTLONG = 36;
|
||||
public final static int REFERENCE_DATATYPE_JSON = 200267;
|
||||
public final static int REFERENCE_DATATYPE_TIME = 24;
|
||||
public final static int REFERENCE_DATATYPE_TIMESTAMP_WITH_TIMEZONE = 200133;
|
||||
public final static int REFERENCE_DATATYPE_TIMEZONE = 200135;
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.compiere.util.KeyNamePair;
|
|||
|
||||
/** Generated Model for Test
|
||||
* @author iDempiere (generated)
|
||||
* @version Release 11 - $Id$ */
|
||||
* @version Release 12 - $Id$ */
|
||||
@org.adempiere.base.Model(table="Test")
|
||||
public class X_Test extends PO implements I_Test, I_Persistent
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ public class X_Test extends PO implements I_Test, I_Persistent
|
|||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 20231222L;
|
||||
private static final long serialVersionUID = 20240226L;
|
||||
|
||||
/** Standard Constructor */
|
||||
public X_Test (Properties ctx, int Test_ID, String trxName)
|
||||
|
@ -382,6 +382,22 @@ public class X_Test extends PO implements I_Test, I_Persistent
|
|||
return (String)get_Value(COLUMNNAME_Help);
|
||||
}
|
||||
|
||||
/** Set JSON Data.
|
||||
@param JsonData The json field stores json data.
|
||||
*/
|
||||
public void setJsonData (Object JsonData)
|
||||
{
|
||||
set_Value (COLUMNNAME_JsonData, JsonData);
|
||||
}
|
||||
|
||||
/** Get JSON Data.
|
||||
@return The json field stores json data.
|
||||
*/
|
||||
public Object getJsonData()
|
||||
{
|
||||
return get_Value(COLUMNNAME_JsonData);
|
||||
}
|
||||
|
||||
public I_M_Locator getM_Locator() throws RuntimeException
|
||||
{
|
||||
return (I_M_Locator)MTable.get(getCtx(), I_M_Locator.Table_ID)
|
||||
|
|
|
@ -1140,7 +1140,7 @@ public class DataEngine
|
|||
pde = new PrintDataElement(pdc.getAD_PrintFormatItem_ID(), pdc.getColumnName(), Boolean.valueOf(b), pdc.getDisplayType(), pdc.getFormatPattern());
|
||||
}
|
||||
}
|
||||
else if (pdc.getDisplayType() == DisplayType.TextLong)
|
||||
else if (pdc.getDisplayType() == DisplayType.TextLong || (pdc.getDisplayType() == DisplayType.JSON && DB.isOracle()))
|
||||
{
|
||||
String value = "";
|
||||
if ("java.lang.String".equals(rs.getMetaData().getColumnClassName(counter)))
|
||||
|
|
|
@ -2208,6 +2208,24 @@ public final class DB
|
|||
//
|
||||
return out.toString();
|
||||
} // TO_STRING
|
||||
|
||||
/**
|
||||
* Return string as JSON object for INSERT statements with correct precision
|
||||
* @param value
|
||||
* @return value as json
|
||||
*/
|
||||
public static String TO_JSON (String value)
|
||||
{
|
||||
return s_cc.getDatabase().TO_JSON(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string with right casting for JSON inserts
|
||||
*/
|
||||
public static String getJSONCast()
|
||||
{
|
||||
return s_cc.getDatabase().getJSONCast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method to close result set
|
||||
|
|
|
@ -66,6 +66,7 @@ import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TIMEZONE;
|
|||
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_URL;
|
||||
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_UUID;
|
||||
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_YES_NO;
|
||||
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_JSON;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
|
@ -196,8 +197,9 @@ public final class DisplayType
|
|||
public static final int RecordID = REFERENCE_DATATYPE_RECORD_ID;
|
||||
|
||||
public static final int RecordUU = REFERENCE_DATATYPE_RECORD_UU;
|
||||
|
||||
|
||||
public static final int JSON = REFERENCE_DATATYPE_JSON;
|
||||
|
||||
public static final int TimestampWithTimeZone = REFERENCE_DATATYPE_TIMESTAMP_WITH_TIMEZONE;
|
||||
|
||||
public static final int TimeZoneId = REFERENCE_DATATYPE_TIMEZONE;
|
||||
|
@ -409,7 +411,7 @@ public final class DisplayType
|
|||
public static boolean isText(int displayType)
|
||||
{
|
||||
if (displayType == String || displayType == Text
|
||||
|| displayType == TextLong || displayType == Memo
|
||||
|| displayType == TextLong || displayType == JSON || displayType == Memo
|
||||
|| displayType == FilePath || displayType == FileName
|
||||
|| displayType == URL || displayType == PrinterName
|
||||
|| displayType == SingleSelectionGrid || displayType == Color
|
||||
|
@ -588,7 +590,8 @@ public final class DisplayType
|
|||
public static boolean isLOB (int displayType)
|
||||
{
|
||||
if (displayType == Binary
|
||||
|| displayType == TextLong)
|
||||
|| displayType == TextLong
|
||||
|| (displayType == JSON && DB.isOracle()))
|
||||
return true;
|
||||
|
||||
//not custom type, don't have to check factory
|
||||
|
@ -932,7 +935,7 @@ public final class DisplayType
|
|||
*/
|
||||
public static Class<?> getClass (int displayType, boolean yesNoAsBoolean)
|
||||
{
|
||||
if (isText(displayType) || displayType == List || displayType == Payment || displayType == RadiogroupList)
|
||||
if (isText(displayType) || displayType == List || displayType == Payment || displayType == RadiogroupList || displayType == JSON)
|
||||
return String.class;
|
||||
else if (isID(displayType) || displayType == Integer) // note that Integer is stored as BD
|
||||
return Integer.class;
|
||||
|
@ -972,6 +975,7 @@ public final class DisplayType
|
|||
s_customDisplayTypeNegativeCache.put(customTypeKey, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return Object.class;
|
||||
} // getClass
|
||||
|
@ -1044,7 +1048,9 @@ public final class DisplayType
|
|||
return getDatabase().getNumericDataType()+"(10)";
|
||||
else
|
||||
return getDatabase().getCharacterDataType()+"(" + fieldLength + ")";
|
||||
}
|
||||
}
|
||||
if (displayType == DisplayType.JSON)
|
||||
return getDatabase().getJsonDataType();
|
||||
|
||||
IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType);
|
||||
if (cache != null) {
|
||||
|
@ -1175,6 +1181,8 @@ public final class DisplayType
|
|||
return "Text";
|
||||
case TextLong:
|
||||
return "TextLong";
|
||||
case JSON:
|
||||
return "JSON";
|
||||
case Time:
|
||||
return "Time";
|
||||
case TimestampWithTimeZone:
|
||||
|
|
|
@ -44,8 +44,14 @@ import javax.swing.InputMap;
|
|||
import javax.swing.JComponent;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import org.adempiere.exceptions.AdempiereException;
|
||||
import org.compiere.Adempiere;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.lowagie.text.Document;
|
||||
import com.lowagie.text.DocumentException;
|
||||
import com.lowagie.text.pdf.PdfContentByte;
|
||||
|
@ -783,5 +789,20 @@ public class Util
|
|||
public static boolean isDeveloperMode() {
|
||||
return Files.isDirectory(Paths.get(Adempiere.getAdempiereHome() + File.separator + "org.adempiere.base")) || "Y".equals(System.getProperty("org.idempiere.developermode"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string with a formatted JSON object
|
||||
* @return string with a pretty JSON format
|
||||
*/
|
||||
public static String prettifyJSONString(String value) {
|
||||
Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
|
||||
try {
|
||||
JsonElement jsonElement = JsonParser.parseString(value);
|
||||
return gson.toJson(jsonElement);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new AdempiereException(Msg.getMsg(Env.getCtx(), "InvalidJSON"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // Util
|
||||
|
|
|
@ -397,7 +397,8 @@ public class PoFiller{
|
|||
setInteger(qName);
|
||||
} else if (info.getColumnClass(index) == Timestamp.class) {
|
||||
setTimestamp(qName);
|
||||
}else if(DisplayType.TextLong == info.getColumnDisplayType(index)) {// export column from system have type is normal string, but import to system have this column but type is textlong (mean blob)
|
||||
} else if(DisplayType.TextLong == info.getColumnDisplayType(index) || DisplayType.JSON == info.getColumnDisplayType(index)) {
|
||||
// export column from system have type is normal string, but import to system have this column but type is text long (mean blob)
|
||||
if (getStringValue (qName) != null && !isBlobOnPackinFile(qName)) {
|
||||
setString(qName);
|
||||
}else {
|
||||
|
|
|
@ -1447,6 +1447,7 @@ public class ProcessParameterPanel extends Panel implements
|
|||
if (displayType == DisplayType.Text
|
||||
|| displayType == DisplayType.Memo
|
||||
|| displayType == DisplayType.TextLong
|
||||
|| displayType == DisplayType.JSON
|
||||
|| displayType == DisplayType.Binary
|
||||
|| displayType == DisplayType.RowID
|
||||
|| editor.getGridField().isEncrypted())
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package org.adempiere.webui.editor;
|
||||
|
||||
import org.compiere.model.GridField;
|
||||
import org.compiere.util.Util;
|
||||
|
||||
|
||||
public class WJsonEditor extends WStringEditor {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param gridField
|
||||
*/
|
||||
public WJsonEditor(GridField gridField) {
|
||||
super(gridField);
|
||||
getComponent().setMultiline(true);
|
||||
setChangeEventWhenEditing(false);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param gridField
|
||||
* @param tableEditor
|
||||
* @param editorConfiguration
|
||||
*/
|
||||
public WJsonEditor(GridField gridField, boolean tableEditor, IEditorConfiguration editorConfiguration) {
|
||||
super(gridField, tableEditor, editorConfiguration);
|
||||
getComponent().setMultiline(true);
|
||||
setChangeEventWhenEditing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
super.setValue(value);
|
||||
|
||||
if (value != null && !Util.isEmpty(value.toString()))
|
||||
getComponent().setValue(Util.prettifyJSONString(value.toString()));
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ import org.adempiere.webui.editor.WFileDirectoryEditor;
|
|||
import org.adempiere.webui.editor.WFilenameEditor;
|
||||
import org.adempiere.webui.editor.WHtmlEditor;
|
||||
import org.adempiere.webui.editor.WImageEditor;
|
||||
import org.adempiere.webui.editor.WJsonEditor;
|
||||
import org.adempiere.webui.editor.WLocationEditor;
|
||||
import org.adempiere.webui.editor.WLocatorEditor;
|
||||
import org.adempiere.webui.editor.WNumberEditor;
|
||||
|
@ -240,6 +241,10 @@ public class DefaultEditorFactory implements IEditorFactory {
|
|||
else if (displayType == DisplayType.RecordUU)
|
||||
{
|
||||
editor = new WRecordUUIDEditor(gridField, tableEditor, editorConfiguration);
|
||||
}
|
||||
else if (displayType == DisplayType.JSON)
|
||||
{
|
||||
editor = new WJsonEditor(gridField, tableEditor, editorConfiguration);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -536,7 +536,23 @@ public class DB_Oracle implements AdempiereDatabase
|
|||
}
|
||||
return result.toString();
|
||||
} // TO_NUMBER
|
||||
|
||||
/**
|
||||
* @return string with right casting for JSON inserts
|
||||
*/
|
||||
public String getJSONCast () {
|
||||
return "?";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string as JSON object for INSERT statements
|
||||
* @param value
|
||||
* @return value as json
|
||||
*/
|
||||
public String TO_JSON (String value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL Commands.
|
||||
|
@ -1026,6 +1042,11 @@ public class DB_Oracle implements AdempiereDatabase
|
|||
public String getClobDataType() {
|
||||
return "CLOB";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJsonDataType() {
|
||||
return getClobDataType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTimestampDataType() {
|
||||
|
@ -1072,6 +1093,8 @@ public class DB_Oracle implements AdempiereDatabase
|
|||
// Inline Constraint
|
||||
if (column.getAD_Reference_ID() == DisplayType.YesNo)
|
||||
sql.append(" CHECK (").append(column.getColumnName()).append(" IN ('Y','N'))");
|
||||
else if (column.getAD_Reference_ID() == DisplayType.JSON)
|
||||
sql.append("CONSTRAINT ").append(column.getAD_Table().getTableName()).append("_").append(column.getColumnName()).append("_isjson CHECK (").append(column.getColumnName()).append(" IS JSON)");
|
||||
|
||||
// Null
|
||||
if (column.isMandatory())
|
||||
|
|
|
@ -539,8 +539,31 @@ public class DB_PostgreSQL implements AdempiereDatabase
|
|||
}
|
||||
return result.toString();
|
||||
} // TO_NUMBER
|
||||
|
||||
/**
|
||||
* @return string with right casting for JSON inserts
|
||||
*/
|
||||
public String getJSONCast () {
|
||||
return "CAST (? AS jsonb)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string as JSON object for INSERT statements
|
||||
* @param value
|
||||
* @return value as json
|
||||
*/
|
||||
public String TO_JSON (String value)
|
||||
{
|
||||
if (value == null)
|
||||
return "NULL";
|
||||
|
||||
|
||||
StringBuilder retValue = null;
|
||||
retValue = new StringBuilder("CAST (");
|
||||
retValue.append(value);
|
||||
retValue.append(" AS jsonb)");
|
||||
return retValue.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL Commands
|
||||
* @param cmdType CMD_*
|
||||
|
@ -1211,6 +1234,11 @@ public class DB_PostgreSQL implements AdempiereDatabase
|
|||
public String getClobDataType() {
|
||||
return "TEXT";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJsonDataType() {
|
||||
return "JSONB";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTimestampDataType() {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package org.idempiere.test.base;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.compiere.dbPort.Convert;
|
||||
import org.compiere.model.MTest;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.Ini;
|
||||
import org.idempiere.test.AbstractTestCase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class JsonFieldTest extends AbstractTestCase {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public JsonFieldTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSavingJSONValue() {
|
||||
MTest testPO = new MTest(Env.getCtx(), getClass().getName(), 1, getTrxName());
|
||||
boolean updated;
|
||||
testPO.setJsonData("Testing if JSON allows to save regular strings");
|
||||
updated = testPO.save();
|
||||
assertFalse(updated);
|
||||
|
||||
testPO = new MTest(Env.getCtx(), getClass().getName(), 1, getTrxName());
|
||||
String validJsonString = "{ \"name\": \"iDempiere\", \"id\": 100 }";
|
||||
testPO.setJsonData(validJsonString);
|
||||
updated = testPO.save();
|
||||
assertTrue(updated);
|
||||
|
||||
String validJsonArray= "[ {\"type\": \"mobile\", \"phone\": \"001001\"} , {\"type\": \"fix\", \"phone\": \"002002\"} ]";
|
||||
testPO.setJsonData(validJsonArray);
|
||||
updated = testPO.save();
|
||||
assertTrue(updated);
|
||||
|
||||
testPO.setJsonData(null);
|
||||
updated = testPO.save();
|
||||
assertTrue(updated);
|
||||
|
||||
String fileName = Convert.getMigrationScriptFileName("testLogMigrationScript");
|
||||
String folderPg = Convert.getMigrationScriptFolder("postgresql");
|
||||
String folderOr = Convert.getMigrationScriptFolder("oracle");
|
||||
|
||||
//Test inserting/updating with Values
|
||||
Env.getCtx().setProperty(Ini.P_LOGMIGRATIONSCRIPT, "Y");
|
||||
testPO.setJsonData(validJsonString);
|
||||
updated = testPO.save();
|
||||
assertTrue(updated);
|
||||
|
||||
testPO.setJsonData(validJsonArray);
|
||||
updated = testPO.save();
|
||||
assertTrue(updated);
|
||||
|
||||
Env.getCtx().setProperty(Ini.P_LOGMIGRATIONSCRIPT, "");
|
||||
|
||||
rollback();
|
||||
File file = new File(folderPg + fileName);
|
||||
assertTrue(file.exists(), "Not found: " + folderPg + fileName);
|
||||
file.delete();
|
||||
file = new File(folderOr + fileName);
|
||||
assertTrue(file.exists(), "Not found: " + folderOr + fileName);
|
||||
file.delete();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue