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

co.elastic.clients.json.JsonpDeserializerBase Maven / Gradle / Ivy

There is a newer version: 8.17.0
Show newest version
/*
 * Licensed to Elasticsearch B.V. under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch B.V. licenses this file to you 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.
 */

package co.elastic.clients.json;

import jakarta.json.JsonNumber;
import jakarta.json.JsonValue;
import jakarta.json.stream.JsonParser;
import jakarta.json.stream.JsonParser.Event;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Base class for {@link JsonpDeserializer} implementations that accept a set of JSON events known at instanciation time.
 */
public abstract class JsonpDeserializerBase implements JsonpDeserializer {

    private final EnumSet acceptedEvents;
    private final EnumSet nativeEvents;

    protected JsonpDeserializerBase(EnumSet acceptedEvents) {
        this(acceptedEvents, acceptedEvents);
    }

    protected JsonpDeserializerBase(EnumSet acceptedEvents, EnumSet nativeEvents) {
        this.acceptedEvents = acceptedEvents;
        this.nativeEvents = nativeEvents;
    }

    /** Combines accepted events from a number of deserializers */
    protected static EnumSet allAcceptedEvents(JsonpDeserializer... deserializers) {
        EnumSet result = EnumSet.noneOf(Event.class);
        for (JsonpDeserializer deserializer: deserializers) {

            EnumSet set = deserializer.acceptedEvents();
            // Disabled for now. Only happens with the experimental Union2 and is caused by string and number
            // parsers leniency. Need to be replaced with a check on a preferred event type.
            //if (!Collections.disjoint(result, set)) {
            //    throw new IllegalArgumentException("Deserializer accepted events are not disjoint");
            //}

            result.addAll(set);
        }
        return result;
    }

    @Override
    public EnumSet nativeEvents() {
        return nativeEvents;
    }

    /**
     * The JSON events this deserializer accepts as a starting point
     */
    public final EnumSet acceptedEvents() {
        return acceptedEvents;
    }

    /**
     * Convenience method for {@code acceptedEvents.contains(event)}
     */
    public final boolean accepts(Event event) {
        return acceptedEvents.contains(event);
    }

    //---------------------------------------------------------------------------------------------

    //----- Builtin types

    static final JsonpDeserializer STRING =
        // String parsing is lenient and accepts any other primitive type
        new JsonpDeserializerBase(EnumSet.of(
                Event.KEY_NAME, Event.VALUE_STRING, Event.VALUE_NUMBER,
                Event.VALUE_FALSE, Event.VALUE_TRUE
            ),
            EnumSet.of(Event.VALUE_STRING)
        ) {
            @Override
            public String deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_TRUE) {
                    return "true";
                }
                if (event == Event.VALUE_FALSE) {
                    return "false";
                }
                return parser.getString(); // also accepts numbers
            }
        };

    static final JsonpDeserializer INTEGER =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_STRING),
            EnumSet.of(Event.VALUE_NUMBER)
        ) {
            @Override
            public Integer deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_STRING) {
                    return Integer.valueOf(parser.getString());
                }
                return parser.getInt();
            }
        };

    static final JsonpDeserializer BOOLEAN =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_FALSE, Event.VALUE_TRUE, Event.VALUE_STRING),
            EnumSet.of(Event.VALUE_FALSE, Event.VALUE_TRUE)
        ) {
            @Override
            public Boolean deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_STRING) {
                    return Boolean.parseBoolean(parser.getString());
                } else {
                    return event == Event.VALUE_TRUE;
                }
            }
        };

    static final JsonpDeserializer LONG =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_STRING),
            EnumSet.of(Event.VALUE_NUMBER)
        ) {
            @Override
            public Long deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_STRING) {
                    return Long.valueOf(parser.getString());
                }
                return parser.getLong();
            }
        };

    static final JsonpDeserializer FLOAT =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_STRING),
            EnumSet.of(Event.VALUE_NUMBER)

        ) {
            @Override
            public Float deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_STRING) {
                    return Float.valueOf(parser.getString());
                }
                return parser.getBigDecimal().floatValue();
            }
        };

    static final JsonpDeserializer DOUBLE =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_STRING),
            EnumSet.of(Event.VALUE_NUMBER)
        ) {
            @Override
            public Double deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_STRING) {
                    return Double.valueOf(parser.getString());
                }
                return parser.getBigDecimal().doubleValue();
            }
        };

    static final class DoubleOrNullDeserializer extends JsonpDeserializerBase {
        static final EnumSet nativeEvents = EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_NULL);
        static final EnumSet acceptedEvents = EnumSet.of(Event.VALUE_STRING, Event.VALUE_NUMBER, Event.VALUE_NULL);
        private final double defaultValue;

        DoubleOrNullDeserializer(double defaultValue) {
            super(acceptedEvents, nativeEvents);
            this.defaultValue = defaultValue;
        }

        @Override
        public Double deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
            if (event == Event.VALUE_NULL) {
                return defaultValue;
            }
            if (event == Event.VALUE_STRING) {
                return Double.valueOf(parser.getString());
            }
            return parser.getBigDecimal().doubleValue();
        }
    }

    static final class IntOrNullDeserializer extends JsonpDeserializerBase {
        static final EnumSet nativeEvents = EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_NULL);
        static final EnumSet acceptedEvents = EnumSet.of(Event.VALUE_STRING, Event.VALUE_NUMBER, Event.VALUE_NULL);
        private final int defaultValue;

        IntOrNullDeserializer(int defaultValue) {
            super(acceptedEvents, nativeEvents);
            this.defaultValue = defaultValue;
        }

        @Override
        public Integer deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
            if (event == Event.VALUE_NULL) {
                return defaultValue;
            }
            if (event == Event.VALUE_STRING) {
                return Integer.valueOf(parser.getString());
            }
            return parser.getInt();
        }
    }

    static final JsonpDeserializer DOUBLE_OR_NAN =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_STRING, Event.VALUE_NULL),
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_NULL)
        ) {
            @Override
            public Double deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_NULL) {
                    return Double.NaN;
                }
                if (event == Event.VALUE_STRING) {
                    return Double.valueOf(parser.getString());
                }
                return parser.getBigDecimal().doubleValue();
            }
        };

    static final JsonpDeserializer NUMBER =
        new JsonpDeserializerBase(
            EnumSet.of(Event.VALUE_NUMBER, Event.VALUE_STRING),
            EnumSet.of(Event.VALUE_NUMBER)
        ) {
            @Override
            public Number deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                if (event == Event.VALUE_STRING) {
                    return Double.valueOf(parser.getString());
                }
                return ((JsonNumber)parser.getValue()).numberValue();
            }
        };

    static final JsonpDeserializer JSON_VALUE =
        new JsonpDeserializerBase(
            EnumSet.allOf(Event.class)
        ) {
            @Override
            public JsonValue deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
                return parser.getValue();
            }
        };

    static final JsonpDeserializer VOID = new JsonpDeserializerBase(
        EnumSet.allOf(Event.class)
    ) {
        @Override
        public Void deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
            JsonpUtils.skipValue(parser, event);
            return null;
        }
    };

    //----- Collections

    static class ArrayDeserializer implements JsonpDeserializer> {
        private final JsonpDeserializer itemDeserializer;
        private EnumSet acceptedEvents;
        private static final EnumSet nativeEvents = EnumSet.of(Event.START_ARRAY);

        protected ArrayDeserializer(JsonpDeserializer itemDeserializer) {
            this.itemDeserializer = itemDeserializer;
        }

        @Override
        public EnumSet nativeEvents() {
            return nativeEvents;
        }

        @Override
        public EnumSet acceptedEvents() {
            // Accepted events is computed lazily
            // no need for double-checked lock, we don't care about computing it several times
            if (acceptedEvents == null) {
                acceptedEvents = EnumSet.of(Event.START_ARRAY);
                acceptedEvents.addAll(itemDeserializer.acceptedEvents());
            }
            return acceptedEvents;
        }

        @Override
        public List deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
            if (event == Event.START_ARRAY) {
                List result = new ArrayList<>();
                try {
                    while ((event = parser.next()) != Event.END_ARRAY) {
                        // JSON null: add null unless the deserializer can handle it
                        if (event == Event.VALUE_NULL && !itemDeserializer.accepts(event)) {
                            result.add(null);
                        } else {
                            JsonpUtils.ensureAccepts(itemDeserializer, parser, event);
                            result.add(itemDeserializer.deserialize(parser, mapper, event));
                        }
                    }
                } catch (Exception e) {
                    throw JsonpMappingException.from(e, result.size(), parser);
                }
                return result;
            } else {
                // Single-value mode
                JsonpUtils.ensureAccepts(itemDeserializer, parser, event);
                return Collections.singletonList(itemDeserializer.deserialize(parser, mapper, event));
            }
        }
    }

    static class StringMapDeserializer extends JsonpDeserializerBase> {
        private final JsonpDeserializer itemDeserializer;

        protected StringMapDeserializer(JsonpDeserializer itemDeserializer) {
            super(EnumSet.of(Event.START_OBJECT));
            this.itemDeserializer = itemDeserializer;
        }

        @Override
        public Map deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
            Map result = new HashMap<>();
            String key = null;
            try {
                while ((event = parser.next()) != Event.END_OBJECT) {
                    JsonpUtils.expectEvent(parser, Event.KEY_NAME, event);
                    key = parser.getString();
                    T value = itemDeserializer.deserialize(parser, mapper);
                    result.put(key, value);
                }
            } catch (Exception e) {
                throw JsonpMappingException.from(e, null, key, parser);
            }
            return result;
        }
    }

    static class EnumMapDeserializer extends JsonpDeserializerBase> {
        private final JsonpDeserializer keyDeserializer;
        private final JsonpDeserializer valueDeserializer;

        protected EnumMapDeserializer(JsonpDeserializer keyDeserializer, JsonpDeserializer valueDeserializer) {
            super(EnumSet.of(Event.START_OBJECT));
            this.keyDeserializer = keyDeserializer;
            this.valueDeserializer = valueDeserializer;
        }

        @Override
        public Map deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
            Map result = new HashMap<>();
            String keyName = null;
            try {
                while ((event = parser.next()) != Event.END_OBJECT) {
                    JsonpUtils.expectEvent(parser, Event.KEY_NAME, event);
                    keyName = parser.getString();
                    K key = keyDeserializer.deserialize(parser, mapper, event);
                    V value = valueDeserializer.deserialize(parser, mapper);
                    result.put(key, value);
                }
            } catch (Exception e) {
                throw JsonpMappingException.from(e, null, keyName, parser);
            }
            return result;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy