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.