1 package com.vladmihalcea.hibernate.type.util;
2
3 import com.fasterxml.jackson.core.JsonGenerator;
4 import com.fasterxml.jackson.core.JsonParser;
5 import com.fasterxml.jackson.core.JsonProcessingException;
6 import com.fasterxml.jackson.databind.*;
7 import com.fasterxml.jackson.databind.module.SimpleModule;
8 import org.hibernate.HibernateException;
9
10 import java.io.IOException;
11 import java.io.Serializable;
12 import java.lang.reflect.Type;
13 import java.time.OffsetDateTime;
14 import java.time.ZoneOffset;
15 import java.time.format.DateTimeParseException;
16 import java.util.Date;
17
18 import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
19
20 /**
21  * Wraps a Jackson {@link ObjectMapper} so that you can supply your own {@link ObjectMapper} reference.
22  *
23  * @author Vlad Mihalcea
24  * @since 2.1.0
25  */

26 public class ObjectMapperWrapper implements Serializable {
27
28     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
29         .findAndRegisterModules()
30         .registerModule(
31             new SimpleModule()
32                 .addSerializer(OffsetDateTime.class, OffsetDateTimeSerializer.INSTANCE)
33                 .addDeserializer(OffsetDateTime.class, OffsetDateTimeDeserializer.INSTANCE)
34         );
35
36     public static final ObjectMapperWrapper INSTANCE = new ObjectMapperWrapper();
37
38     private ObjectMapper objectMapper;
39
40     private ObjectMapperSupplier objectMapperSupplier;
41
42     private JsonSerializer jsonSerializer;
43
44     public ObjectMapperWrapper() {
45         this(OBJECT_MAPPER);
46     }
47
48     public ObjectMapperWrapper(ObjectMapperSupplier objectMapperSupplier) {
49         this.objectMapperSupplier = objectMapperSupplier;
50     }
51
52     public ObjectMapperWrapper(ObjectMapper objectMapper) {
53         this.objectMapper = objectMapper;
54         this.jsonSerializer = new ObjectMapperJsonSerializer(this);
55     }
56
57     public void setJsonSerializer(JsonSerializer jsonSerializer) {
58         this.jsonSerializer = jsonSerializer;
59     }
60
61     public ObjectMapper getObjectMapper() {
62         if(objectMapper == null && objectMapperSupplier != null) {
63             objectMapper = objectMapperSupplier.get();
64         }
65         if(objectMapper == null) {
66             throw new HibernateException("The provided ObjectMapper is null!");
67         }
68         return objectMapper;
69     }
70
71     public <T> T fromString(String string, Class<T> clazz) {
72         try {
73             return getObjectMapper().readValue(string, clazz);
74         } catch (IOException e) {
75             throw new HibernateException(
76                 new IllegalArgumentException("The given string value: " + string + " cannot be transformed to Json object", e)
77             );
78         }
79     }
80
81     public <T> T fromString(String string, Type type) {
82         try {
83             return getObjectMapper().readValue(string, getObjectMapper().getTypeFactory().constructType(type));
84         } catch (IOException e) {
85             throw new HibernateException(
86                 new IllegalArgumentException("The given string value: " + string + " cannot be transformed to Json object", e)
87             );
88         }
89     }
90
91     public <T> T fromBytes(byte[] value, Class<T> clazz) {
92         try {
93             return getObjectMapper().readValue(value, clazz);
94         } catch (IOException e) {
95             throw new HibernateException(
96                 new IllegalArgumentException("The given byte array cannot be transformed to Json object", e)
97             );
98         }
99     }
100
101     public <T> T fromBytes(byte[] value, Type type) {
102         try {
103             return getObjectMapper().readValue(value, getObjectMapper().getTypeFactory().constructType(type));
104         } catch (IOException e) {
105             throw new HibernateException(
106                 new IllegalArgumentException("The given byte array cannot be transformed to Json object", e)
107             );
108         }
109     }
110
111     public String toString(Object value) {
112         try {
113             return getObjectMapper().writeValueAsString(value);
114         } catch (JsonProcessingException e) {
115             throw new HibernateException(
116                 new IllegalArgumentException("The given Json object value: " + value + " cannot be transformed to a String", e)
117             );
118         }
119     }
120
121     public byte[] toBytes(Object value) {
122         try {
123             return getObjectMapper().writeValueAsBytes(value);
124         } catch (JsonProcessingException e) {
125             throw new HibernateException(
126                 new IllegalArgumentException("The given Json object value: " + value + " cannot be transformed to a byte array", e)
127             );
128         }
129     }
130
131     public JsonNode toJsonNode(String value) {
132         try {
133             return getObjectMapper().readTree(value);
134         } catch (IOException e) {
135             throw new HibernateException(
136                 new IllegalArgumentException(e)
137             );
138         }
139     }
140
141     public <T> T clone(T value) {
142         return jsonSerializer.clone(value);
143     }
144
145     public static class OffsetDateTimeSerializer extends com.fasterxml.jackson.databind.JsonSerializer<OffsetDateTime> {
146
147         public static final OffsetDateTimeSerializer INSTANCE = new OffsetDateTimeSerializer();
148
149         @Override
150         public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
151             if (offsetDateTime == null) {
152                 jsonGenerator.writeNull();
153             } else {
154                 jsonGenerator.writeString(offsetDateTime.format(ISO_OFFSET_DATE_TIME));
155             }
156         }
157
158         @Override
159         public Class<OffsetDateTime> handledType() {
160             return OffsetDateTime.class;
161         }
162     }
163
164     public static class OffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> {
165
166         public static final OffsetDateTimeDeserializer INSTANCE = new OffsetDateTimeDeserializer();
167
168         @Override
169         public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
170             if (jsonParser.getText() != null) {
171                 try {
172                     return OffsetDateTime.parse(jsonParser.getText(), ISO_OFFSET_DATE_TIME);
173                 } catch (DateTimeParseException e) {
174                     Date date = new Date((long) jsonParser.getDoubleValue() * 1000);
175                     return date.toInstant().atOffset(ZoneOffset.UTC);
176                 }
177             }
178             return null;
179         }
180
181         @Override
182         public Class<OffsetDateTime> handledType() {
183             return OffsetDateTime.class;
184         }
185     }
186 }
187