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

hydra.langs.json.JsonDecoding Maven / Gradle / Ivy

package hydra.langs.json;

import hydra.json.Value;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * Decoding functions for Hydra's native JSON values which are intended for use in Java.
 * Decoding failures result in Java exceptions rather than failure flows.
 */
public abstract class JsonDecoding {
    /**
     * Decode a list from JSON.
     */
    public static  List decodeList(Function mapping, Value json) {
        return json.accept(new Value.PartialVisitor>() {
            @Override
            public List otherwise(Value instance) {
                throw unexpected("array", instance);
            }

            @Override
            public List visit(Value.Array instance) {
                return instance.value.stream().map(mapping).collect(Collectors.toList());
            }

            @Override
            public List visit(Value.Null instance) {
                return Collections.emptyList();
            }
        });
    }

    /**
     * Decode a boolean value from JSON.
     */
    public static boolean decodeBoolean(Value json) {
        return json.accept(new Value.PartialVisitor() {
            @Override
            public Boolean otherwise(Value instance) {
                throw unexpected("boolean", instance);
            }

            @Override
            public Boolean visit(Value.Boolean_ instance) {
                return instance.value;
            }
        });
    }

    /**
     * Decode a double value from JSON.
     */
    public static double decodeDouble(Value json) {
        Number num = decodeNumber(json);
        return num.doubleValue();
    }

    /**
     * Decode a float value from JSON.
     */
    public static float decodeFloat(Value json) {
        Number num = decodeNumber(json);
        return num.floatValue();
    }

    /**
     * Decode an integer value from JSON.
     */
    public static int decodeInteger(Value json) {
        Number num = decodeNumber(json);
        return num.intValue();
    }

    /**
     * Decode a list field from JSON.
     */
    public static  List decodeListField(String name, Function mapping, Value json) {
        // Note: this allows the field to be omitted, and also allows a null value, both resulting in an empty list
        Optional> opt = decodeOptionalField(name, v -> decodeList(mapping, v), json, Collections.emptyList());
        return opt.orElse(Collections.emptyList());
    }

    /**
     * Decode a number from JSON.
     */
    public static double decodeNumber(Value json) {
        return json.accept(new Value.PartialVisitor() {
            @Override
            public Double otherwise(Value instance) {
                throw unexpected("number", instance);
            }

            @Override
            public Double visit(Value.Number_ instance) {
                return instance.value;
            }
        });
    }

    /**
     * Decode an object (key/value map) from JSON.
     */
    public static Map decodeObject(Value json) {
        return json.accept(new Value.PartialVisitor>() {
            @Override
            public Map otherwise(Value instance) {
                throw unexpected("object", instance);
            }

            @Override
            public Map visit(Value.Object_ instance) {
                return instance.value;
            }
        });
    }

    /**
     * Decode an optional double-valued field from JSON.
     */
    public static Optional decodeOptionalDoubleField(String name, Value json) {
        return decodeOptionalField(name, JsonDecoding::decodeDouble, json, null);
    }

    /**
     * Decode an optional field from JSON.
     */
    public static  Optional decodeOptionalField(String name,
                                                      Function mapping,
                                                      Value json,
                                                      A defaultValue) {
        Map map = decodeObject(json);
        Value fieldValue = map.get(name);
        if (fieldValue == null) {
            return defaultValue == null ? Optional.empty() : Optional.of(defaultValue);
        } else {
            return fieldValue.accept(new Value.PartialVisitor>() {
                @Override
                public Optional otherwise(Value instance) {
                    return Optional.of(mapping.apply(fieldValue));
                }

                @Override
                public Optional visit(Value.Null instance) {
                    return defaultValue == null ? Optional.empty() : Optional.of(defaultValue);
                }
            });
        }
    }

    /**
     * Decode an optional field from JSON.
     */
    public static  Optional decodeOptionalField(String name, Function mapping, Value json) {
        return decodeOptionalField(name, mapping, json, null);
    }

    /**
     * Decode an optional integer-valued field from JSON.
     */
    public static Optional decodeOptionalIntegerField(String name, Value json) {
        return decodeOptionalField(name, JsonDecoding::decodeInteger, json, null);
    }

    /**
     * Decode an optional set-valued field from JSON.
     */
    public static  Optional> decodeOptionalSetField(String name, Function mapping, Value json) {
        return decodeOptionalField(name, v -> decodeSet(mapping, v), json);
    }

    /**
     * Decode a required field from JSON.
     */
    public static  A decodeRequiredField(String name, Function mapping, Value json, A defaultValue) {
        Optional opt = decodeOptionalField(name, mapping, json);
        if (opt.isPresent()) {
            return opt.get();
        } else {
            throw new RuntimeException("missing required field \"" + name + "\"");
        }
    }

    /**
     * Decode a required field from JSON.
     */
    public static  A decodeRequiredField(String name, Function mapping, Value json) {
        return decodeRequiredField(name, mapping, json, null);
    }

    /**
     * Decode a required list-valued field from JSON.
     */
    public static  List decodeRequiredListField(String name, Function mapping, Value json) {
        return decodeRequiredField(name, v -> decodeList(mapping, v), json, Collections.emptyList());
    }

    /**
     * Decode a set from JSON.
     */
    public static  Set decodeSet(Function mapping, Value json) {
        return new HashSet<>(decodeList(mapping, json));
    }

    /**
     * Decode a string value from JSON.
     */
    public static String decodeString(Value json) {
        return json.accept(new Value.PartialVisitor() {
            @Override
            public String otherwise(Value instance) {
                throw unexpected("string", instance);
            }

            @Override
            public String visit(Value.String_ instance) {
                return instance.value;
            }
        });
    }

    /**
     * Decode a list of string values from JSON.
     */
    public static List decodeStringList(Value json) {
        return decodeList(JsonDecoding::decodeString, json);
    }

    /**
     * Decode an enumerated value from JSON.
     */
    public static  A decodeEnum(Map values, Value json) {
        String key = decodeString(json);
        A value = values.get(key);
        if (value == null) {
            throw new RuntimeException("no such enum value: " + key);
        } else {
            return value;
        }
    }

    /**
     * Decode a union (injection) from JSON.
     */
    public static  A decodeUnion(Map> mappings, Value json) {
        return json.accept(new Value.PartialVisitor() {
            @Override
            public A otherwise(Value instance) {
                throw unexpected("string or object", instance);
            }

            @Override
            public A visit(Value.Object_ instance) {
                Map map = instance.value;
                if (map.size() != 1) {
                    throw new RuntimeException("expected union, found object with " + map.size() + " fields");
                }
                Map.Entry entry = map.entrySet().iterator().next();
                Function mapping = mappings.get(entry.getKey());
                if (mapping == null) {
                    throw new RuntimeException("unexpected union value: " + entry.getKey());
                } else {
                    return mapping.apply(entry.getValue());
                }
            }

            @Override
            public A visit(Value.String_ instance) {
                Function mapping = mappings.get(instance.value);
                if (mapping == null) {
                    throw new RuntimeException("unexpected union value: " + instance.value);
                } else {
                    return mapping.apply(new Value.Null());
                }
            }
        });
    }

    /**
     * Fail on an unexpected JSON value.
     */
    public static RuntimeException unexpected(String expected, Value actual) {
        return new RuntimeException("expected " + expected + ", found " + actual);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy