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

com.bazaarvoice.emodb.common.json.JsonValidator Maven / Gradle / Ivy

package com.bazaarvoice.emodb.common.json;

import com.bazaarvoice.emodb.common.json.deferred.LazyJsonMap;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

public class JsonValidator {

    public static Object checkValid(@Nullable Object obj) {
        if (obj == null || obj instanceof String || obj instanceof Boolean) {
            // ok!
        } else if (obj instanceof Number) {
            if (!isSupportedNumberType(obj)) {
                throw new JsonValidationException("Unsupported JSON numeric type: " + obj + " (class=" + obj.getClass().getName() + ")");
            }
        } else if (obj instanceof List) {
            for (Object value : ((List) obj)) {
                checkValid(value);
            }
        } else if (obj instanceof LazyJsonMap) {
            LazyJsonMap lazyJsonMap = (LazyJsonMap) obj;
            if (lazyJsonMap.isDeserialized()) {
                // Map is already deserialized so it can be validated as a whole with no deserialization cost.
                validateMap(lazyJsonMap);
            } else {
                // The serialized JSON string contained in the instance should already be valid.
                // For efficiency only validate any attributes on top of the original JSON.  Otherwise, validating each
                // entry in the map could cause an unnecessary and expensive deserialization.
                Map overrides = lazyJsonMap.getOverrides();
                if (overrides != null) {
                    validateMap(overrides);
                }
            }
        } else if (obj instanceof Map) {
            validateMap((Map) obj);
        } else {
            throw new JsonValidationException("Unsupported JSON value type: " + obj + " (class=" + obj.getClass().getName() + ")");
        }
        return obj;
    }

    private static void validateMap(Map map) {
        for (Map.Entry entry : map.entrySet()) {
            Object key = entry.getKey();
            if (!(key instanceof String)) {
                throw new JsonValidationException("JSON map keys must be instances of String: " + key + " (class=" + (key != null ? key.getClass().getName() : "null") + ")");
            }
            checkValid(entry.getValue());
        }
    }

    private static boolean isSupportedNumberType(Object obj) {
        if (obj instanceof Integer || obj instanceof Long || obj instanceof Byte || obj instanceof Short) {
            return true;
        }
        if (obj instanceof Double) {
            Double num = (Double) obj;
            return !num.isInfinite() && !num.isNaN();
        }
        if (obj instanceof Float) {
            Float num = (Float) obj;
            return !num.isInfinite() && !num.isNaN();
        }
        if (obj instanceof BigInteger) {
            // The parser supports up to 64-bit Longs then it switches to Double.
            BigInteger num = (BigInteger) obj;
            return obj.equals(BigInteger.valueOf(num.longValue())) || !Double.isInfinite(num.doubleValue());
        }
        if (obj instanceof BigDecimal) {
            // Big decimal doesn't support infinity or nan so no problems there.  We will round it to double precision though.
            return false;  // todo: we could support some BigDecimal values, but it's tricky to determine which ones will overflow/underflow.
        }
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy