11.3 (although will probably work on 11.x)
I'm trying to merge two json objects together. The code below seems to work, but I was wondering if there is any other (faster, more efficient) way.
/** merge source object into target object
* @param : source json object
* @param : target json object
* @returns : void
*/
method void mergeJson(oSource as JsonObject,oTarget as JsonObject):
def var lv_i as int no-undo.
def var lv_name as char extent no-undo.
/** get all the defined properties of the source object */
assign lv_name = oSource:getnames().
/** loop through all properties of the source object
* and try to add the property to the target object
* using no-error to supress any error if the target
* object already has the property defined
*/
do lv_i = 1 to extent(lv_name):
/** add to the target object using the appropriate getter on the source object */
case oSource:GetType(lv_name[lv_i]):
when JsonDataType:ARRAY then oTarget:add(lv_name[lv_i],oSource:GetJsonArray(lv_name[lv_i])) no-error.
when JsonDataType:BOOLEAN then oTarget:add(lv_name[lv_i],oSource:GetLogical(lv_name[lv_i])) no-error.
when JsonDataType:NUMBER then oTarget:add(lv_name[lv_i],oSource:GetDecimal(lv_name[lv_i])) no-error.
when JsonDataType:OBJECT then oTarget:add(lv_name[lv_i],oSource:GetJsonObject(lv_name[lv_i])) no-error.
when JsonDataType:string then oTarget:add(lv_name[lv_i],oSource:GetCharacter(lv_name[lv_i])) no-error.
/** yikes. throw a wobbly */
otherwise undo, throw new AppError(substitute("Unknown json datatype &1",oSource:GetType(lv_name[lv_i])),0).
end case.
end.
end method.
actually, there's a nasty little bug hiding in here
getType() returns jsonDataType:NUMBER for both integer and decimal
so, you don't know to use getInteger() or getDecimal() .. which is a problem ..
let's say that the property in source is an integer. this means that
oTarget:add(lv_name[lv_i],oSource:Decimal(lv_name[lv_i]))
would add a decimal property to the target
but if I used
oTarget:add(lv_name[lv_i],oSource:GetInteger(lv_name[lv_i]))
on a decimal property on the source , the add fails because I tried to get a integer ..
so, the question now is
not knowing the structure of the json file, and having to rely on GetType() to determine the type of the property. how can I add a decimal field if the source property is decimal, and a n integer property if the source property is integer ??
You can use AddNumber as follows:
when JsonDataType:NUMBER then
pcTarget:AddNumber(cNames[i],string(pcSource:GetDecimal(cNames[i]))).
Note that this will remove any unnecessary decimals (2.0 will be output as 2). .If you want to keep the decimal delimiter also when it is 0 you would need to read as integer or int64 first and use decimal if it fails:
when JsonDataType:NUMBER then
do:
intval = ?.
intval = pcSource:GetInt64(cNames[i]) no-error.
if intval <> ? then
pcTarget:Add(cNames[i],intval).
else
pcTarget:Add(cNames[i],pcSource:GetDecimal(cNames[i])).
end.
--
I'm trying to merge two json objects together. The code below seems to work, but I was wondering if there is any other (faster, more efficient) way.
I believe this is the only way to merge Json nodes in ABL. You should probably wrap your merge with a Has check instead of using Add with no-error and also check for NULLs.
do lv_i = 1 to extent(lv_name):
/** add to the target object using the appropriate getter on the source object */
if not oTarget:Has(lv_name[lv_i]) then
do:
case oSource:GetType(lv_name[lv_i]):
when JsonDataType:ARRAY then oTarget:Add(lv_name[lv_i],oSource:GetJsonArray(lv_name[lv_i])).
when JsonDataType:BOOLEAN then oTarget:Add(lv_name[lv_i],oSource:GetLogical(lv_name[lv_i])).
when JsonDataType:NUMBER then oTarget:Add(lv_name[lv_i],oSource:GetDecimal(lv_name[lv_i])).
when JsonDataType:OBJECT then oTarget:Add(lv_name[lv_i],oSource:GetJsonObject(lv_name[lv_i])).
when JsonDataType:STRING then oTarget:Add(lv_name[lv_i],oSource:GetCharacter(lv_name[lv_i])).
when JsonDataType:NULL then oTarget:AddNull(lv_name[lv_i]).
end case.
end.
end.
yp.