src/main/java/com/cedarsoftware/util/io/MapResolver.java
public void traverseFields(final Deque<JsonObject<String, Object>> stack, final JsonObject<String, Object> jsonObj)
    {
        final Object target = jsonObj.target;
        for (Map.Entry<String, Object> e : jsonObj.entrySet())
        {
            final String fieldName = e.getKey();
            final Field field = (target != null) ? MetaUtils.getField(target.getClass(), fieldName) : null;
            final Object rhs = e.getValue();

            if (rhs == null)
            {
                jsonObj.put(fieldName, null);
            }
            else if (rhs == JsonParser.EMPTY_OBJECT)
            {
                jsonObj.put(fieldName, new JsonObject());
            }
            else if (rhs.getClass().isArray())
            {   // RHS is an array
                // Trace the contents of the array (so references inside the array and into the array work)
                JsonObject<String, Object> jsonArray = new JsonObject<String, Object>();
                jsonArray.put("@items", rhs);
                stack.addFirst(jsonArray);

                // Assign the array directly to the Map key (field name)
                jsonObj.put(fieldName, rhs);
            }
            else if (rhs instanceof JsonObject)
            {
                JsonObject<String, Object> jObj = (JsonObject) rhs;

                if (field != null && MetaUtils.isLogicalPrimitive(field.getType()))
                {
                    jObj.put("value", MetaUtils.convert(field.getType(), jObj.get("value")));
                    continue;
                }
                Long refId = jObj.getReferenceId();

                if (refId != null)
                {    // Correct field references
                    JsonObject refObject = getReferencedObj(refId);
                    jsonObj.put(fieldName, refObject);    // Update Map-of-Maps reference
                }
                else
                {
                    stack.addFirst(jObj);
                }
            }
            else if (field != null)
            {   // The code below is 'upgrading' the RHS values in the passed in JsonObject Map
                // by using the @type class name (when specified and exists), to coerce the vanilla
                // JSON values into the proper types defined by the class listed in @type.  This is
                // a cool feature of json-io, that even when reading a map-of-maps JSON file, it will
                // improve the final types of values in the maps RHS, to be of the field type that
                // was optionally specified in @type.
                final Class fieldType = field.getType();
                if (MetaUtils.isPrimitive(fieldType) || BigDecimal.class.equals(fieldType) || BigInteger.class.equals(fieldType) || Date.class.equals(fieldType))
                {
                    jsonObj.put(fieldName, MetaUtils.convert(fieldType, rhs));
                }
                else if (rhs instanceof String)
                {
                    if (fieldType != String.class && fieldType != StringBuilder.class && fieldType != StringBuffer.class)
                    {
                        if ("".equals(((String)rhs).trim()))
                        {   // Allow "" to null out a non-String field on the inbound JSON
                            jsonObj.put(fieldName, null);
                        }
                    }
                }
            }
        }
        jsonObj.target = null;  // don't waste space (used for typed return, not for Map return)
    }
public void traverseFields(final Deque<JsonObject<String, Object>> stack, final JsonObject<String, Object> jsonObj)
    {
        final Object target = jsonObj.target;
        for (Map.Entry<String, Object> e : jsonObj.entrySet())
        {
            final String fieldName = e.getKey();
            final Field field = (target != null) ? MetaUtils.getField(target.getClass(), fieldName) : null;
            final Object rhs = e.getValue();

            if (rhs == null)
            {
                jsonObj.put(fieldName, null);
            }
            else if (rhs == JsonParser.EMPTY_OBJECT)
            {
                jsonObj.put(fieldName, new JsonObject());
            }
            else if (rhs.getClass().isArray())
            {   // RHS is an array
                // Trace the contents of the array (so references inside the array and into the array work)
                JsonObject<String, Object> jsonArray = new JsonObject<String, Object>();
                jsonArray.put("@items", rhs);
                stack.addFirst(jsonArray);

                // Assign the array directly to the Map key (field name)
                jsonObj.put(fieldName, rhs);
            }
            else if (rhs instanceof JsonObject)
            {
                JsonObject<String, Object> jObj = (JsonObject) rhs;

                if (field != null && MetaUtils.isLogicalPrimitive(field.getType()))
                {
                    jObj.put("value", MetaUtils.convert(field.getType(), jObj.get("value")));
                    continue;
                }
                Long refId = jObj.getReferenceId();

                if (refId != null)
                {    // Correct field references
                    JsonObject refObject = getReferencedObj(refId);
                    jsonObj.put(fieldName, refObject);    // Update Map-of-Maps reference
                }
                else
                {
                    stack.addFirst(jObj);
                }
            }
            else if (field != null)
            {   // The code below is 'upgrading' the RHS values in the passed in JsonObject Map
                // by using the @type class name (when specified and exists), to coerce the vanilla
                // JSON values into the proper types defined by the class listed in @type.  This is
                // a cool feature of json-io, that even when reading a map-of-maps JSON file, it will
                // improve the final types of values in the maps RHS, to be of the field type that
                // was optionally specified in @type.
                final Class fieldType = field.getType();
                if (MetaUtils.isPrimitive(fieldType) || BigDecimal.class.equals(fieldType) || BigInteger.class.equals(fieldType) || Date.class.equals(fieldType))
                {
                    jsonObj.put(fieldName, MetaUtils.convert(fieldType, rhs));
                }
                else if (rhs instanceof String)
                {
                    if (fieldType != String.class && fieldType != StringBuilder.class && fieldType != StringBuffer.class)
                    {
                        if ("".equals(((String)rhs).trim()))
                        {   // Allow "" to null out a non-String field on the inbound JSON
                            jsonObj.put(fieldName, null);
                        }
                    }
                }
            }
        }
        jsonObj.target = null;  // don't waste space (used for typed return, not for Map return)
    }
public void traverseFields ( final Deque < JsonObject < String , Object > > stack , final JsonObject < String , Object > jsonObj ) { final Object target = jsonObj . target ; for ( Map . Entry < String , Object > e : jsonObj . entrySet ( ) ) { final String fieldName = e . getKey ( ) ; final Field field = ( target != null ) ? MetaUtils . getField ( target . getClass ( ) , fieldName ) : null ; final Object rhs = e . getValue ( ) ; if ( rhs == null ) { jsonObj . put ( fieldName , null ) ; } else if ( rhs == JsonParser . EMPTY_OBJECT ) { jsonObj . put ( fieldName , new JsonObject ( ) ) ; } else if ( rhs . getClass ( ) . isArray ( ) ) { // RHS is an array // Trace the contents of the array (so references inside the array and into the array work) JsonObject < String , Object > jsonArray = new JsonObject < String , Object > ( ) ; jsonArray . put ( "@items" , rhs ) ; stack . addFirst ( jsonArray ) ; // Assign the array directly to the Map key (field name) jsonObj . put ( fieldName , rhs ) ; } else if ( rhs instanceof JsonObject ) { JsonObject < String , Object > jObj = ( JsonObject ) rhs ; if ( field != null && MetaUtils . isLogicalPrimitive ( field . getType ( ) ) ) { jObj . put ( "value" , MetaUtils . convert ( field . getType ( ) , jObj . get ( "value" ) ) ) ; continue ; } Long refId = jObj . getReferenceId ( ) ; if ( refId != null ) { // Correct field references JsonObject refObject = getReferencedObj ( refId ) ; jsonObj . put ( fieldName , refObject ) ; // Update Map-of-Maps reference } else { stack . addFirst ( jObj ) ; } } else if ( field != null ) { // The code below is 'upgrading' the RHS values in the passed in JsonObject Map // by using the @type class name (when specified and exists), to coerce the vanilla // JSON values into the proper types defined by the class listed in @type.  This is // a cool feature of json-io, that even when reading a map-of-maps JSON file, it will // improve the final types of values in the maps RHS, to be of the field type that // was optionally specified in @type. final Class fieldType = field . getType ( ) ; if ( MetaUtils . isPrimitive ( fieldType ) || BigDecimal . class . equals ( fieldType ) || BigInteger . class . equals ( fieldType ) || Date . class . equals ( fieldType ) ) { jsonObj . put ( fieldName , MetaUtils . convert ( fieldType , rhs ) ) ; } else if ( rhs instanceof String ) { if ( fieldType != String . class && fieldType != StringBuilder . class && fieldType != StringBuffer . class ) { if ( "" . equals ( ( ( String ) rhs ) . trim ( ) ) ) { // Allow "" to null out a non-String field on the inbound JSON jsonObj . put ( fieldName , null ) ; } } } } } jsonObj . target = null ; // don't waste space (used for typed return, not for Map return) }
Walk the JsonObject fields and perform necessary substitutions so that all references matched up.
This code patches @ref and @id pairings up, in the 'Map of Map' mode.  Where the JSON may contain
an @id of an object which can have more than one @ref to it, this code will make sure that each
@ref (value side of the Map associated to a given field name) will be pointer to the appropriate Map
instance.
@param stack   Stack (Deque) used for graph traversal.
@param jsonObj a Map-of-Map representation of the current object being examined (containing all fields).
Walk the JsonObject fields and perform necessary substitutions so that all references matched up . This code patches