All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cedarsoftware.util.io.MapResolver Maven / Gradle / Ivy

package com.cedarsoftware.util.io;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Map;

/**
 * The MapResolver converts the raw Maps created from the JsonParser to higher
 * quality Maps representing the implied object graph.  It does this by replace
 * @ref values with the Map with an @id key with the same value.
 *
 * This approach 'rewires' the original object graph.  During the resolution process,
 * if 'peer' classes can be found for given Maps (for example, an @type entry is
 * available which indicates the class that would have been associated to the Map,
 * then the associated class is consulted to help 'improve' the quality of the primitive
 * values within the map fields.  For example, if the peer class indicated that a field
 * was of type 'short', and the Map had a long value (JSON only returns long's for integer
 * types), then the long would be converted to a short.
 *
 * The final Map representation is a very high-quality graph that represents the original
 * JSON graph.  It can be passed as input to JsonWriter, and the JsonWriter will write
 * out the equivalent JSON to what was originally read.  This technique allows json-io to
 * be used on a machine that does not have any of the Java classes from the original graph,
 * read it in a JSON graph (any JSON graph), return the equivalent maps, allow mutations of
 * those maps, and finally this graph can be written out.
 *
 * @author John DeRegnaucourt ([email protected])
 *         
* Copyright (c) Cedar Software LLC *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class MapResolver extends Resolver { protected MapResolver(JsonReader reader) { super(reader); } protected void traverseFields(final Deque> stack, final JsonObject jsonObj) { final Object target = jsonObj.target; for (Map.Entry 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 jsonArray = new JsonObject(); 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 jObj = (JsonObject) rhs; if (field != null && JsonObject.isPrimitiveWrapper(field.getType())) { jObj.put("value", MetaUtils.newPrimitiveWrapper(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)) { jsonObj.put(fieldName, MetaUtils.newPrimitiveWrapper(fieldType, rhs)); } else if (BigDecimal.class == fieldType) { jsonObj.put(fieldName, Readers.bigDecimalFrom(rhs)); } else if (BigInteger.class == fieldType) { jsonObj.put(fieldName, Readers.bigIntegerFrom(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) } /** * Process java.util.Collection and it's derivatives. Collections are written specially * so that the serialization does not expose the Collection's internal structure, for * example a TreeSet. All entries are processed, except unresolved references, which * are filled in later. For an indexable collection, the unresolved references are set * back into the proper element location. For non-indexable collections (Sets), the * unresolved references are added via .add(). * @param stack a Stack (Deque) used to support graph traversal. * @param jsonObj a Map-of-Map representation of the JSON input stream. */ protected void traverseCollection(final Deque> stack, final JsonObject jsonObj) { final Object[] items = jsonObj.getArray(); if (items == null || items.length == 0) { return; } int idx = 0; final List copy = new ArrayList(items.length); for (Object element : items) { if (element == JsonParser.EMPTY_OBJECT) { copy.add(new JsonObject()); continue; } copy.add(element); if (element instanceof Object[]) { // array element inside Collection JsonObject jsonObject = new JsonObject(); jsonObject.put("@items", element); stack.addFirst(jsonObject); } else if (element instanceof JsonObject) { JsonObject jsonObject = (JsonObject) element; Long refId = jsonObject.getReferenceId(); if (refId != null) { // connect reference JsonObject refObject = getReferencedObj(refId); copy.set(idx, refObject); } else { stack.addFirst(jsonObject); } } idx++; } jsonObj.target = null; // don't waste space (used for typed return, not generic Map return) for (int i=0; i < items.length; i++) { items[i] = copy.get(i); } } protected void traverseArray(Deque> stack, JsonObject jsonObj) { traverseCollection(stack, jsonObj); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy