001/*
002 * Copyright 2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.batch.item.json;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.InputStreamReader;
022
023import com.google.gson.Gson;
024import com.google.gson.JsonIOException;
025import com.google.gson.JsonSyntaxException;
026import com.google.gson.stream.JsonReader;
027import com.google.gson.stream.JsonToken;
028
029import org.springframework.batch.item.ParseException;
030import org.springframework.core.io.Resource;
031import org.springframework.util.Assert;
032
033/**
034 * Implementation of {@link JsonObjectReader} based on
035 * <a href="https://github.com/google/gson">Google Gson</a>.
036 *
037 * @param <T> type of the target object
038 *
039 * @author Mahmoud Ben Hassine
040 * @since 4.1
041 */
042public class GsonJsonObjectReader<T> implements JsonObjectReader<T> {
043
044        private Class<? extends T> itemType;
045
046        private JsonReader jsonReader;
047
048        private Gson mapper = new Gson();
049
050        private InputStream inputStream;
051
052        /**
053         * Create a new {@link GsonJsonObjectReader} instance.
054         * @param itemType the target item type
055         */
056        public GsonJsonObjectReader(Class<? extends T> itemType) {
057                this.itemType = itemType;
058        }
059
060        /**
061         * Set the object mapper to use to map Json objects to domain objects.
062         * @param mapper the object mapper to use
063         */
064        public void setMapper(Gson mapper) {
065                Assert.notNull(mapper, "The mapper must not be null");
066                this.mapper = mapper;
067        }
068
069        @Override
070        public void open(Resource resource) throws Exception {
071                Assert.notNull(resource, "The resource must not be null");
072                this.inputStream = resource.getInputStream();
073                this.jsonReader = this.mapper.newJsonReader(new InputStreamReader(this.inputStream));
074                Assert.state(this.jsonReader.peek() == JsonToken.BEGIN_ARRAY,
075                                "The Json input stream must start with an array of Json objects");
076                this.jsonReader.beginArray();
077        }
078
079        @Override
080        public T read() throws Exception {
081                try {
082                        if (this.jsonReader.hasNext()) {
083                                return this.mapper.fromJson(this.jsonReader, this.itemType);
084                        }
085                } catch (IOException |JsonIOException | JsonSyntaxException e) {
086                        throw new ParseException("Unable to read next JSON object", e);
087                }
088                return null;
089        }
090
091        @Override
092        public void close() throws Exception {
093                this.inputStream.close();
094                this.jsonReader.close();
095        }
096
097}