1616
1717package org .dizitart .no2 .mapper .jackson ;
1818
19+ import static org .dizitart .no2 .common .util .ValidationUtils .notNull ;
1920import com .fasterxml .jackson .annotation .JsonAutoDetect ;
20- import com .fasterxml .jackson .core .JsonParser ;
21- import com .fasterxml .jackson .databind .Module ;
22- import com .fasterxml .jackson .databind .*;
2321import org .dizitart .no2 .NitriteConfig ;
2422import org .dizitart .no2 .collection .Document ;
2523import org .dizitart .no2 .common .mapper .NitriteMapper ;
26- import org .dizitart .no2 .mapper .jackson .modules .NitriteIdModule ;
2724import org .dizitart .no2 .exceptions .ObjectMappingException ;
2825import org .dizitart .no2 .exceptions .ValidationException ;
29-
30- import java .io .IOException ;
31- import java .util .*;
32-
33- import static org .dizitart .no2 .common .util .ValidationUtils .notNull ;
26+ import org .dizitart .no2 .mapper .jackson .modules .NitriteIdModule ;
27+ import tools .jackson .core .json .JsonReadFeature ;
28+ import tools .jackson .databind .DatabindException ;
29+ import tools .jackson .databind .JacksonModule ;
30+ import tools .jackson .databind .JsonNode ;
31+ import tools .jackson .databind .ObjectMapper ;
32+ import tools .jackson .databind .json .JsonMapper ;
33+
34+ import java .util .ArrayList ;
35+ import java .util .LinkedHashMap ;
36+ import java .util .List ;
37+ import java .util .Map ;
38+ import java .util .stream .Collectors ;
3439
3540/**
3641 * A {@link org.dizitart.no2.common.mapper.NitriteMapper} implementation that uses Jackson ObjectMapper to
3742 * convert objects to and from Nitrite document.
38- *
39- * @since 4.0
40- * @see org.dizitart.no2.common.mapper.NitriteMapper
43+ *
4144 * @author Anindya Chatterjee
45+ * @see org.dizitart.no2.common.mapper.NitriteMapper
46+ * @since 4.0
4247 */
4348public class JacksonMapper implements NitriteMapper {
49+ private final List <JacksonModule > modules = new ArrayList <>();
4450 private ObjectMapper objectMapper ;
4551
4652 /**
@@ -50,30 +56,37 @@ public class JacksonMapper implements NitriteMapper {
5056 */
5157 protected ObjectMapper getObjectMapper () {
5258 if (objectMapper == null ) {
53- objectMapper = new ObjectMapper ();
54- objectMapper .setVisibility (
55- objectMapper .getSerializationConfig ().getDefaultVisibilityChecker ()
56- .withFieldVisibility (JsonAutoDetect .Visibility .ANY )
57- .withGetterVisibility (JsonAutoDetect .Visibility .NONE )
58- .withIsGetterVisibility (JsonAutoDetect .Visibility .NONE ));
59- objectMapper .configure (JsonParser .Feature .ALLOW_UNQUOTED_FIELD_NAMES , true );
60- objectMapper .configure (JsonParser .Feature .ALLOW_SINGLE_QUOTES , true );
61- objectMapper .configure (JsonParser .Feature .ALLOW_COMMENTS , true );
62- objectMapper .configure (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false );
63- objectMapper .registerModule (new NitriteIdModule ());
59+ objectMapper = JsonMapper .builder ()
60+ .changeDefaultVisibility (visibilityChecker -> visibilityChecker
61+ .withFieldVisibility (JsonAutoDetect .Visibility .ANY )
62+ .withGetterVisibility (JsonAutoDetect .Visibility .NONE )
63+ .withIsGetterVisibility (JsonAutoDetect .Visibility .NONE )
64+ )
65+ .configure (JsonReadFeature .ALLOW_UNQUOTED_PROPERTY_NAMES , true )
66+ .configure (JsonReadFeature .ALLOW_SINGLE_QUOTES , true )
67+ .configure (JsonReadFeature .ALLOW_JAVA_COMMENTS , true )
68+ .addModule (new NitriteIdModule ())
69+ .addModules (modules )
70+ .build ();
71+ modules .clear ();
6472 }
6573 return objectMapper ;
6674 }
6775
6876 /**
6977 * Registers a Jackson module with the object mapper.
78+ * Can only register modules as long as the underlying ObjectMapper is still un-initialized.
7079 *
7180 * @param module the Jackson module to register
81+ *
7282 * @throws ValidationException if the module is null
7383 */
74- public void registerJacksonModule (Module module ) {
84+ public void registerJacksonModule (JacksonModule module ) {
85+ if (objectMapper != null ) {
86+ throw new IllegalStateException ("Can not register modules after object mapper initialization." );
87+ }
7588 notNull (module , "module cannot be null" );
76- getObjectMapper (). registerModule (module );
89+ modules . add (module );
7790 }
7891
7992 /**
@@ -86,11 +99,13 @@ public void registerJacksonModule(Module module) {
8699 * already a Document, converts it to the target type. If the conversion fails,
87100 * throws an ObjectMappingException.
88101 *
89- * @param source the source object to convert
90- * @param type the target type to convert to
102+ * @param source the source object to convert
103+ * @param type the target type to convert to
91104 * @param <Source> the type of the source object
92105 * @param <Target> the type of the target object
106+ *
93107 * @return the converted object of the target type
108+ *
94109 * @throws ObjectMappingException if the conversion fails
95110 */
96111 @ Override
@@ -101,8 +116,9 @@ public <Source, Target> Object tryConvert(Source source, Class<Target> type) {
101116
102117 try {
103118 JsonNode node = getObjectMapper ().convertValue (source , JsonNode .class );
104- if (node == null )
119+ if (node == null ) {
105120 return null ;
121+ }
106122
107123 if (node .isValueNode ()) {
108124 return getNodeValue (node );
@@ -115,11 +131,11 @@ public <Source, Target> Object tryConvert(Source source, Class<Target> type) {
115131 }
116132 } catch (Exception e ) {
117133 throw new ObjectMappingException ("Failed to convert object of type "
118- + source .getClass () + " to type " + type , e );
134+ + source .getClass () + " to type " + type , e );
119135 }
120136
121137 throw new ObjectMappingException ("Can't convert object to type " + type
122- + ", try registering a jackson Module for it." );
138+ + ", try registering a jackson Module for it." );
123139 }
124140
125141 @ Override
@@ -130,21 +146,23 @@ public void initialize(NitriteConfig nitriteConfig) {
130146 * Converts a Nitrite Document to an object of the specified class type using
131147 * Jackson ObjectMapper.
132148 *
133- * @param source the Nitrite Document to be converted
134- * @param type the class type of the object to be converted to
149+ * @param source the Nitrite Document to be converted
150+ * @param type the class type of the object to be converted to
135151 * @param <Target> the type of the object to be converted to
152+ *
136153 * @return the converted object of the specified class type
154+ *
137155 * @throws ObjectMappingException if there is an error in the object mapping
138- * process
156+ * process
139157 */
140158 protected <Target > Target convertFromDocument (Document source , Class <Target > type ) {
141159 try {
142160 return getObjectMapper ().convertValue (source , type );
143161 } catch (IllegalArgumentException iae ) {
144- if (iae .getCause () instanceof JsonMappingException ) {
145- JsonMappingException jme = (JsonMappingException ) iae .getCause ();
146- if (jme .getMessage ().contains ("Cannot construct instance" )) {
147- throw new ObjectMappingException (jme .getMessage ());
162+ if (iae .getCause () instanceof DatabindException ) {
163+ DatabindException cause = (DatabindException ) iae .getCause ();
164+ if (cause .getMessage ().contains ("Cannot construct instance" )) {
165+ throw new ObjectMappingException (cause .getMessage ());
148166 }
149167 }
150168 throw iae ;
@@ -155,8 +173,9 @@ protected <Target> Target convertFromDocument(Document source, Class<Target> typ
155173 * Converts the given source object to a Nitrite {@link Document} using
156174 * Jackson's {@link ObjectMapper}.
157175 *
158- * @param source the source object to convert
176+ * @param source the source object to convert
159177 * @param <Source> the type of the source object
178+ *
160179 * @return the converted Nitrite {@link Document}
161180 */
162181 protected <Source > Document convertToDocument (Source source ) {
@@ -170,7 +189,7 @@ private <T> T getNodeValue(JsonNode node) {
170189 case NUMBER :
171190 return (T ) node .numberValue ();
172191 case STRING :
173- return (T ) node .textValue ();
192+ return (T ) node .stringValue ();
174193 case BOOLEAN :
175194 return (T ) Boolean .valueOf (node .booleanValue ());
176195 default :
@@ -180,9 +199,7 @@ private <T> T getNodeValue(JsonNode node) {
180199
181200 private Document readDocument (JsonNode node ) {
182201 Map <String , Object > objectMap = new LinkedHashMap <>();
183- Iterator <Map .Entry <String , JsonNode >> fields = node .fields ();
184- while (fields .hasNext ()) {
185- Map .Entry <String , JsonNode > entry = fields .next ();
202+ for (Map .Entry <String , JsonNode > entry : node .properties ()) {
186203 String name = entry .getKey ();
187204 JsonNode value = entry .getValue ();
188205 Object object = readObject (value );
@@ -193,47 +210,34 @@ private Document readDocument(JsonNode node) {
193210 }
194211
195212 private Object readObject (JsonNode node ) {
196- if (node == null )
197- return null ;
198- try {
199- switch (node .getNodeType ()) {
200- case ARRAY :
201- return readArray (node );
202- case BINARY :
203- return node .binaryValue ();
204- case BOOLEAN :
205- return node .booleanValue ();
206- case MISSING :
207- case NULL :
208- return null ;
209- case NUMBER :
210- return node .numberValue ();
211- case OBJECT :
212- case POJO :
213- return readDocument (node );
214- case STRING :
215- return node .textValue ();
216- }
217- } catch (IOException e ) {
213+ if (node == null ) {
218214 return null ;
219215 }
220- return null ;
216+
217+ switch (node .getNodeType ()) {
218+ case ARRAY :
219+ return readArray (node );
220+ case BINARY :
221+ return node .binaryValue ();
222+ case BOOLEAN :
223+ return node .booleanValue ();
224+ case NUMBER :
225+ return node .numberValue ();
226+ case OBJECT :
227+ case POJO :
228+ return readDocument (node );
229+ case STRING :
230+ return node .stringValue ();
231+ default :
232+ return null ;
233+ }
221234 }
222235
223- @ SuppressWarnings ({ "unchecked" , "rawtypes" })
224- private List readArray (JsonNode array ) {
236+ private List <Object > readArray (JsonNode array ) {
225237 if (array .isArray ()) {
226- List list = new ArrayList ();
227- Iterator iterator = array .elements ();
228- while (iterator .hasNext ()) {
229- Object element = iterator .next ();
230- if (element instanceof JsonNode ) {
231- list .add (readObject ((JsonNode ) element ));
232- } else {
233- list .add (element );
234- }
235- }
236- return list ;
238+ return array .valueStream ()
239+ .map (this ::readObject )
240+ .collect (Collectors .toList ());
237241 }
238242 return null ;
239243 }
0 commit comments