1 package com.fasterxml.jackson.databind.node;
2
3 import java.io.*;
4 import java.math.BigDecimal;
5 import java.math.BigInteger;
6 import java.util.*;
7
8 import com.fasterxml.jackson.core.*;
9 import com.fasterxml.jackson.core.type.WritableTypeId;
10 import com.fasterxml.jackson.databind.*;
11 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
12 import com.fasterxml.jackson.databind.util.RawValue;
13
14 /**
15  * Node that maps to JSON Object structures in JSON content.
16  *<p>
17  * Note: class was <code>final</code> temporarily for Jackson 2.2.
18  */

19 public class ObjectNode
20     extends ContainerNode<ObjectNode>
21     implements java.io.Serializable
22 {
23     private static final long serialVersionUID = 1L; // since 2.10
24
25     // Note: LinkedHashMap for backwards compatibility
26     protected final Map<String, JsonNode> _children;
27
28     public ObjectNode(JsonNodeFactory nc) {
29         super(nc);
30         _children = new LinkedHashMap<String, JsonNode>();
31     }
32
33     /**
34      * @since 2.4
35      */

36     public ObjectNode(JsonNodeFactory nc, Map<String, JsonNode> kids) {
37         super(nc);
38         _children = kids;
39     }
40
41     @Override
42     protected JsonNode _at(JsonPointer ptr) {
43         return get(ptr.getMatchingProperty());
44     }
45
46     /* Question: should this delegate to `JsonNodeFactory`? It does not absolutely
47      * have to, as long as sub-types override the method but...
48      */

49     // note: co-variant for type safety
50     @SuppressWarnings("unchecked")
51     @Override
52     public ObjectNode deepCopy()
53     {
54         ObjectNode ret = new ObjectNode(_nodeFactory);
55
56         for (Map.Entry<String, JsonNode> entry: _children.entrySet())
57             ret._children.put(entry.getKey(), entry.getValue().deepCopy());
58
59         return ret;
60     }
61
62     /*
63     /**********************************************************
64     /* Overrides for JsonSerializable.Base
65     /**********************************************************
66      */

67
68     @Override
69     public boolean isEmpty(SerializerProvider serializers) {
70         return _children.isEmpty();
71     }
72
73     /*
74     /**********************************************************
75     /* Implementation of core JsonNode API
76     /**********************************************************
77      */

78
79     @Override
80     public JsonNodeType getNodeType() {
81         return JsonNodeType.OBJECT;
82     }
83
84     @Override
85     public final boolean isObject() {
86         return true;
87     }
88     
89     @Override public JsonToken asToken() { return JsonToken.START_OBJECT; }
90
91     @Override
92     public int size() {
93         return _children.size();
94     }
95
96     @Override // since 2.10
97     public boolean isEmpty() { return _children.isEmpty(); }
98     
99     @Override
100     public Iterator<JsonNode> elements() {
101         return _children.values().iterator();
102     }
103
104     @Override
105     public JsonNode get(int index) { return null; }
106
107     @Override
108     public JsonNode get(String fieldName) {
109         return _children.get(fieldName);
110     }
111
112     @Override
113     public Iterator<String> fieldNames() {
114         return _children.keySet().iterator();
115     }
116
117     @Override
118     public JsonNode path(int index) {
119         return MissingNode.getInstance();
120     }
121
122     @Override
123     public JsonNode path(String fieldName)
124     {
125         JsonNode n = _children.get(fieldName);
126         if (n != null) {
127             return n;
128         }
129         return MissingNode.getInstance();
130     }
131
132     @Override
133     public JsonNode required(String fieldName) {
134         JsonNode n = _children.get(fieldName);
135         if (n != null) {
136             return n;
137         }
138         return _reportRequiredViolation("No value for property '%s' of `ObjectNode`", fieldName);
139     }
140
141     /**
142      * Method to use for accessing all fields (with both names
143      * and values) of this JSON Object.
144      */

145     @Override
146     public Iterator<Map.Entry<String, JsonNode>> fields() {
147         return _children.entrySet().iterator();
148     }
149
150     @SuppressWarnings("unchecked")
151     @Override
152     public ObjectNode with(String propertyName) {
153         JsonNode n = _children.get(propertyName);
154         if (n != null) {
155             if (n instanceof ObjectNode) {
156                 return (ObjectNode) n;
157             }
158             throw new UnsupportedOperationException("Property '" + propertyName
159                 + "' has value that is not of type ObjectNode (but " + n
160                 .getClass().getName() + ")");
161         }
162         ObjectNode result = objectNode();
163         _children.put(propertyName, result);
164         return result;
165     }
166
167     @SuppressWarnings("unchecked")
168     @Override
169     public ArrayNode withArray(String propertyName)
170     {
171         JsonNode n = _children.get(propertyName);
172         if (n != null) {
173             if (n instanceof ArrayNode) {
174                 return (ArrayNode) n;
175             }
176             throw new UnsupportedOperationException("Property '" + propertyName
177                 + "' has value that is not of type ArrayNode (but " + n
178                 .getClass().getName() + ")");
179         }
180         ArrayNode result = arrayNode();
181         _children.put(propertyName, result);
182         return result;
183     }
184
185     @Override
186     public boolean equals(Comparator<JsonNode> comparator, JsonNode o)
187     {
188         if (!(o instanceof ObjectNode)) {
189             return false;
190         }
191         ObjectNode other = (ObjectNode) o;
192         Map<String, JsonNode> m1 = _children;
193         Map<String, JsonNode> m2 = other._children;
194
195         final int len = m1.size();
196         if (m2.size() != len) {
197             return false;
198         }
199
200         for (Map.Entry<String, JsonNode> entry : m1.entrySet()) {
201             JsonNode v2 = m2.get(entry.getKey());
202             if ((v2 == null) || !entry.getValue().equals(comparator, v2)) {
203                 return false;
204             }
205         }
206         return true;
207     }
208
209     /*
210     /**********************************************************
211     /* Public API, finding value nodes
212     /**********************************************************
213      */

214     
215     @Override
216     public JsonNode findValue(String fieldName)
217     {
218         for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
219             if (fieldName.equals(entry.getKey())) {
220                 return entry.getValue();
221             }
222             JsonNode value = entry.getValue().findValue(fieldName);
223             if (value != null) {
224                 return value;
225             }
226         }
227         return null;
228     }
229     
230     @Override
231     public List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar)
232     {
233         for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
234             if (fieldName.equals(entry.getKey())) {
235                 if (foundSoFar == null) {
236                     foundSoFar = new ArrayList<JsonNode>();
237                 }
238                 foundSoFar.add(entry.getValue());
239             } else { // only add children if parent not added
240                 foundSoFar = entry.getValue().findValues(fieldName, foundSoFar);
241             }
242         }
243         return foundSoFar;
244     }
245
246     @Override
247     public List<String> findValuesAsText(String fieldName, List<String> foundSoFar)
248     {
249         for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
250             if (fieldName.equals(entry.getKey())) {
251                 if (foundSoFar == null) {
252                     foundSoFar = new ArrayList<String>();
253                 }
254                 foundSoFar.add(entry.getValue().asText());
255             } else { // only add children if parent not added
256                 foundSoFar = entry.getValue().findValuesAsText(fieldName,
257                     foundSoFar);
258             }
259         }
260         return foundSoFar;
261     }
262     
263     @Override
264     public ObjectNode findParent(String fieldName)
265     {
266         for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
267             if (fieldName.equals(entry.getKey())) {
268                 return this;
269             }
270             JsonNode value = entry.getValue().findParent(fieldName);
271             if (value != null) {
272                 return (ObjectNode) value;
273             }
274         }
275         return null;
276     }
277
278     @Override
279     public List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar)
280     {
281         for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
282             if (fieldName.equals(entry.getKey())) {
283                 if (foundSoFar == null) {
284                     foundSoFar = new ArrayList<JsonNode>();
285                 }
286                 foundSoFar.add(this);
287             } else { // only add children if parent not added
288                 foundSoFar = entry.getValue()
289                     .findParents(fieldName, foundSoFar);
290             }
291         }
292         return foundSoFar;
293     }
294     
295     /*
296     /**********************************************************
297     /* Public API, serialization
298     /**********************************************************
299      */

300
301     /**
302      * Method that can be called to serialize this node and
303      * all of its descendants using specified JSON generator.
304      */

305     @Override
306     public void serialize(JsonGenerator g, SerializerProvider provider)
307         throws IOException
308     {
309         @SuppressWarnings("deprecation")
310         boolean trimEmptyArray = (provider != null) &&
311                 !provider.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
312         g.writeStartObject(this);
313         for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
314             /* 17-Feb-2009, tatu: Can we trust that all nodes will always
315              *   extend BaseJsonNode? Or if not, at least implement
316              *   JsonSerializable? Let's start with former, change if
317              *   we must.
318              */

319             BaseJsonNode value = (BaseJsonNode) en.getValue();
320
321             // as per [databind#867], see if WRITE_EMPTY_JSON_ARRAYS feature is disabled,
322             // if the feature is disabled, then should not write an empty array
323             // to the output, so continue to the next element in the iteration
324             if (trimEmptyArray && value.isArray() && value.isEmpty(provider)) {
325                 continue;
326             }
327             g.writeFieldName(en.getKey());
328             value.serialize(g, provider);
329         }
330         g.writeEndObject();
331     }
332
333     @Override
334     public void serializeWithType(JsonGenerator g, SerializerProvider provider,
335             TypeSerializer typeSer)
336         throws IOException
337     {
338         @SuppressWarnings("deprecation")
339         boolean trimEmptyArray = (provider != null) &&
340                 !provider.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
341
342         WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
343                 typeSer.typeId(this, JsonToken.START_OBJECT));
344         for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
345             BaseJsonNode value = (BaseJsonNode) en.getValue();
346
347             // check if WRITE_EMPTY_JSON_ARRAYS feature is disabled,
348             // if the feature is disabled, then should not write an empty array
349             // to the output, so continue to the next element in the iteration
350             if (trimEmptyArray && value.isArray() && value.isEmpty(provider)) {
351                 continue;
352             }
353             
354             g.writeFieldName(en.getKey());
355             value.serialize(g, provider);
356         }
357         typeSer.writeTypeSuffix(g, typeIdDef);
358     }
359
360     /*
361     /**********************************************************
362     /* Extended ObjectNode API, mutators, since 2.1
363     /**********************************************************
364      */

365
366     /**
367      * Method that will set specified field, replacing old value, if any.
368      * Note that this is identical to {@link #replace(String, JsonNode)},
369      * except for return value.
370      *<p>
371      * NOTE: added to replace those uses of {@link #put(String, JsonNode)}
372      * where chaining with 'this' is desired.
373      *<p>
374      * NOTE: co-variant return type since 2.10
375      *
376      * @param value to set field to; if null, will be converted
377      *   to a {@link NullNode} first  (to remove field entry, call
378      *   {@link #remove} instead)
379      *
380      * @return This node after adding/replacing property value (to allow chaining)
381      *
382      * @since 2.1
383      */

384     @SuppressWarnings("unchecked")
385     public <T extends JsonNode> T set(String fieldName, JsonNode value)
386     {
387         if (value == null) {
388             value = nullNode();
389         }
390         _children.put(fieldName, value);
391         return (T) this;
392     }
393
394     /**
395      * Method for adding given properties to this object node, overriding
396      * any existing values for those properties.
397      *<p>
398      * NOTE: co-variant return type since 2.10
399      * 
400      * @param properties Properties to add
401      * 
402      * @return This node after adding/replacing property values (to allow chaining)
403      *
404      * @since 2.1
405      */

406     @SuppressWarnings("unchecked")
407     public <T extends JsonNode> T setAll(Map<String,? extends JsonNode> properties)
408     {
409         for (Map.Entry<String,? extends JsonNode> en : properties.entrySet()) {
410             JsonNode n = en.getValue();
411             if (n == null) {
412                 n = nullNode();
413             }
414             _children.put(en.getKey(), n);
415         }
416         return (T) this;
417     }
418
419     /**
420      * Method for adding all properties of the given Object, overriding
421      * any existing values for those properties.
422      *<p>
423      * NOTE: co-variant return type since 2.10
424      * 
425      * @param other Object of which properties to add to this object
426      *
427      * @return This node after addition (to allow chaining)
428      *
429      * @since 2.1
430      */

431     @SuppressWarnings("unchecked")
432     public <T extends JsonNode> T setAll(ObjectNode other)
433     {
434         _children.putAll(other._children);
435         return (T) this;
436     }
437
438     /**
439      * Method for replacing value of specific property with passed
440      * value, and returning value (or null if none).
441      *
442      * @param fieldName Property of which value to replace
443      * @param value Value to set property to, replacing old value if any
444      * 
445      * @return Old value of the property; null if there was no such property
446      *   with value
447      * 
448      * @since 2.1
449      */

450     public JsonNode replace(String fieldName, JsonNode value)
451     {
452         if (value == null) { // let's not store 'raw' nulls but nodes
453             value = nullNode();
454         }
455         return _children.put(fieldName, value);
456     }
457
458     /**
459      * Method for removing field entry from this ObjectNode, and
460      * returning instance after removal.
461      *<p>
462      * NOTE: co-variant return type since 2.10
463      * 
464      * @return This node after removing entry (if any)
465      * 
466      * @since 2.1
467      */

468     @SuppressWarnings("unchecked")
469     public <T extends JsonNode> T without(String fieldName)
470     {
471         _children.remove(fieldName);
472         return (T) this;
473     }
474
475     /**
476      * Method for removing specified field properties out of
477      * this ObjectNode.
478      *<p>
479      * NOTE: co-variant return type since 2.10
480      * 
481      * @param fieldNames Names of fields to remove
482      * 
483      * @return This node after removing entries
484      * 
485      * @since 2.1
486      */

487     @SuppressWarnings("unchecked")
488     public <T extends JsonNode> T without(Collection<String> fieldNames)
489     {
490         _children.keySet().removeAll(fieldNames);
491         return (T) this;
492     }
493     
494     /*
495     /**********************************************************
496     /* Extended ObjectNode API, mutators, generic
497     /**********************************************************
498      */

499     
500     /**
501      * Method that will set specified field, replacing old value, if any.
502      *
503      * @param value to set field to; if null, will be converted
504      *   to a {@link NullNode} first  (to remove field entry, call
505      *   {@link #remove} instead)
506      *   
507      * @return Old value of the field, if any; null if there was no
508      *   old value.
509      *   
510      * @deprecated Since 2.4 use either {@link #set(String,JsonNode)} or {@link #replace(String,JsonNode)},
511      */

512     @Deprecated
513     public JsonNode put(String fieldName, JsonNode value)
514     {
515         if (value == null) { // let's not store 'raw' nulls but nodes
516             value = nullNode();
517         }
518         return _children.put(fieldName, value);
519     }
520
521     /**
522      * Method for removing field entry from this ObjectNode.
523      * Will return value of the field, if such field existed;
524      * null if not.
525      * 
526      * @return Value of specified field, if it existed; null if not
527      */

528     public JsonNode remove(String fieldName) {
529         return _children.remove(fieldName);
530     }
531
532     /**
533      * Method for removing specified field properties out of
534      * this ObjectNode.
535      * 
536      * @param fieldNames Names of fields to remove
537      * 
538      * @return This node after removing entries
539      */

540     public ObjectNode remove(Collection<String> fieldNames)
541     {
542         _children.keySet().removeAll(fieldNames);
543         return this;
544     }
545     
546     /**
547      * Method for removing all field properties, such that this
548      * ObjectNode will contain no properties after call.
549      * 
550      * @return This node after removing all entries
551      */

552     @Override
553     public ObjectNode removeAll()
554     {
555         _children.clear();
556         return this;
557     }
558
559     /**
560      * Method for adding given properties to this object node, overriding
561      * any existing values for those properties.
562      * 
563      * @param properties Properties to add
564      * 
565      * @return This node after adding/replacing property values (to allow chaining)
566      * 
567      * @deprecated Since 2.4 use {@link #setAll(Map)},
568      */

569     @Deprecated
570     public JsonNode putAll(Map<String,? extends JsonNode> properties) {
571         return setAll(properties);
572     }
573
574     /**
575      * Method for adding all properties of the given Object, overriding
576      * any existing values for those properties.
577      * 
578      * @param other Object of which properties to add to this object
579      * 
580      * @return This node (to allow chaining)
581      * 
582      * @deprecated Since 2.4 use {@link #setAll(ObjectNode)},
583      */

584     @Deprecated
585     public JsonNode putAll(ObjectNode other) {
586         return setAll(other);
587     }
588
589     /**
590      * Method for removing all field properties out of this ObjectNode
591      * <b>except</b> for ones specified in argument.
592      * 
593      * @param fieldNames Fields to <b>retain</b> in this ObjectNode
594      * 
595      * @return This node (to allow call chaining)
596      */

597     public ObjectNode retain(Collection<String> fieldNames)
598     {
599         _children.keySet().retainAll(fieldNames);
600         return this;
601     }
602
603     /**
604      * Method for removing all field properties out of this ObjectNode
605      * <b>except</b> for ones specified in argument.
606      * 
607      * @param fieldNames Fields to <b>retain</b> in this ObjectNode
608      * 
609      * @return This node (to allow call chaining)
610      */

611     public ObjectNode retain(String... fieldNames) {
612         return retain(Arrays.asList(fieldNames));
613     }
614     
615     /*
616     /**********************************************************
617     /* Extended ObjectNode API, mutators, typed
618     /**********************************************************
619      */

620
621     /**
622      * Method that will construct an ArrayNode and add it as a
623      * field of this ObjectNode, replacing old value, if any.
624      *<p>
625      * <b>NOTE</b>: Unlike all <b>put(...)</b> methods, return value
626      * is <b>NOT</b> this <code>ObjectNode</code>, but the
627      * <b>newly created</b> <code>ArrayNode</code> instance.
628      *
629      * @return Newly constructed ArrayNode (NOT the old value,
630      *   which could be of any type)
631      */

632     public ArrayNode putArray(String fieldName)
633     {
634         ArrayNode n  = arrayNode();
635         _put(fieldName, n);
636         return n;
637     }
638
639     /**
640      * Method that will construct an ObjectNode and add it as a
641      * field of this ObjectNode, replacing old value, if any.
642      *<p>
643      * <b>NOTE</b>: Unlike all <b>put(...)</b> methods, return value
644      * is <b>NOT</b> this <code>ObjectNode</code>, but the
645      * <b>newly created</b> <code>ObjectNode</code> instance.
646      *
647      * @return Newly constructed ObjectNode (NOT the old value,
648      *   which could be of any type)
649      */

650     public ObjectNode putObject(String fieldName)
651     {
652         ObjectNode n = objectNode();
653         _put(fieldName, n);
654         return n;
655     }
656
657     /**
658      * @return This node (to allow chaining)
659      */

660     public ObjectNode putPOJO(String fieldName, Object pojo) {
661         return _put(fieldName, pojoNode(pojo));
662     }
663
664     /**
665      * @since 2.6
666      */

667     public ObjectNode putRawValue(String fieldName, RawValue raw) {
668         return _put(fieldName, rawValueNode(raw));
669     }
670     
671     /**
672      * @return This node (to allow chaining)
673      */

674     public ObjectNode putNull(String fieldName)
675     {
676         _children.put(fieldName, nullNode());
677         return this;
678     }
679
680     /**
681      * Method for setting value of a field to specified numeric value.
682      * 
683      * @return This node (to allow chaining)
684      */

685     public ObjectNode put(String fieldName, short v) {
686         return _put(fieldName, numberNode(v));
687     }
688
689     /**
690      * Alternative method that we need to avoid bumping into NPE issues
691      * with auto-unboxing.
692      * 
693      * @return This node (to allow chaining)
694      */

695     public ObjectNode put(String fieldName, Short v) {
696         return _put(fieldName, (v == null) ? nullNode()
697                 : numberNode(v.shortValue()));
698     }
699
700     /**
701      * Method for setting value of a field to specified numeric value.
702      * The underlying {@link JsonNode} that will be added is constructed
703      * using {@link JsonNodeFactory#numberNode(int)}, and may be
704      *  "smaller" (like {@link ShortNode}) in cases where value fits within
705      *  range of a smaller integral numeric value.
706      * 
707      * @return This node (to allow chaining)
708      */

709     public ObjectNode put(String fieldName, int v) {
710         return _put(fieldName, numberNode(v));
711     }
712
713     /**
714      * Alternative method that we need to avoid bumping into NPE issues
715      * with auto-unboxing.
716      * 
717      * @return This node (to allow chaining)
718      */

719     public ObjectNode put(String fieldName, Integer v) {
720         return _put(fieldName, (v == null) ? nullNode()
721                 : numberNode(v.intValue()));
722     }
723     
724     /**
725      * Method for setting value of a field to specified numeric value.
726      * The underlying {@link JsonNode} that will be added is constructed
727      * using {@link JsonNodeFactory#numberNode(long)}, and may be
728      *  "smaller" (like {@link IntNode}) in cases where value fits within
729      *  range of a smaller integral numeric value.
730      * 
731      * @return This node (to allow chaining)
732      */

733     public ObjectNode put(String fieldName, long v) {
734         return _put(fieldName, numberNode(v));
735     }
736
737     /**
738      * Method for setting value of a field to specified numeric value.
739      * The underlying {@link JsonNode} that will be added is constructed
740      * using {@link JsonNodeFactory#numberNode(Long)}, and may be
741      *  "smaller" (like {@link IntNode}) in cases where value fits within
742      *  range of a smaller integral numeric value.
743      * <p>
744      * Note that this is alternative to {@link #put(String, long)} needed to avoid
745      * bumping into NPE issues with auto-unboxing.
746      * 
747      * @return This node (to allow chaining)
748      */

749     public ObjectNode put(String fieldName, Long v) {
750         return _put(fieldName, (v == null) ? nullNode()
751                 : numberNode(v.longValue()));
752     }
753     
754     /**
755      * Method for setting value of a field to specified numeric value.
756      * 
757      * @return This node (to allow chaining)
758      */

759     public ObjectNode put(String fieldName, float v) {
760         return _put(fieldName, numberNode(v));
761     }
762
763     /**
764      * Alternative method that we need to avoid bumping into NPE issues
765      * with auto-unboxing.
766      * 
767      * @return This node (to allow chaining)
768      */

769     public ObjectNode put(String fieldName, Float v) {
770         return _put(fieldName, (v == null) ? nullNode()
771                 : numberNode(v.floatValue()));
772     }
773     
774     /**
775      * Method for setting value of a field to specified numeric value.
776      * 
777      * @return This node (to allow chaining)
778      */

779     public ObjectNode put(String fieldName, double v) {
780         return _put(fieldName, numberNode(v));
781     }
782
783     /**
784      * Alternative method that we need to avoid bumping into NPE issues
785      * with auto-unboxing.
786      * 
787      * @return This node (to allow chaining)
788      */

789     public ObjectNode put(String fieldName, Double v) {
790         return _put(fieldName, (v == null) ? nullNode()
791                 : numberNode(v.doubleValue()));
792     }
793     
794     /**
795      * Method for setting value of a field to specified numeric value.
796      * 
797      * @return This node (to allow chaining)
798      */

799     public ObjectNode put(String fieldName, BigDecimal v) {
800         return _put(fieldName, (v == null) ? nullNode()
801                 : numberNode(v));
802     }
803
804     /**
805      * Method for setting value of a field to specified numeric value.
806      * 
807      * @return This node (to allow chaining)
808      *
809      * @since 2.9
810      */

811     public ObjectNode put(String fieldName, BigInteger v) {
812         return _put(fieldName, (v == null) ? nullNode()
813                 : numberNode(v));
814     }
815
816     /**
817      * Method for setting value of a field to specified String value.
818      * 
819      * @return This node (to allow chaining)
820      */

821     public ObjectNode put(String fieldName, String v) {
822         return _put(fieldName, (v == null) ? nullNode()
823                 : textNode(v));
824     }
825
826     /**
827      * Method for setting value of a field to specified String value.
828      * 
829      * @return This node (to allow chaining)
830      */

831     public ObjectNode put(String fieldName, boolean v) {
832         return _put(fieldName, booleanNode(v));
833     }
834
835     /**
836      * Alternative method that we need to avoid bumping into NPE issues
837      * with auto-unboxing.
838      * 
839      * @return This node (to allow chaining)
840      */

841     public ObjectNode put(String fieldName, Boolean v) {
842         return _put(fieldName, (v == null) ? nullNode()
843                 : booleanNode(v.booleanValue()));
844     }
845     
846     /**
847      * Method for setting value of a field to specified binary value
848      * 
849      * @return This node (to allow chaining)
850      */

851     public ObjectNode put(String fieldName, byte[] v) {
852         return _put(fieldName, (v == null) ? nullNode()
853                 : binaryNode(v));
854     }
855     
856     /*
857     /**********************************************************
858     /* Standard methods
859     /**********************************************************
860      */

861
862     @Override
863     public boolean equals(Object o)
864     {
865         if (o == thisreturn true;
866         if (o == nullreturn false;
867         if (o instanceof ObjectNode) {
868             return _childrenEqual((ObjectNode) o);
869         }
870         return false;
871     }
872
873     /**
874      * @since 2.3
875      */

876     protected boolean _childrenEqual(ObjectNode other)
877     {
878         return _children.equals(other._children);
879     }
880     
881     @Override
882     public int hashCode()
883     {
884         return _children.hashCode();
885     }
886
887     /*
888     /**********************************************************
889     /* Internal methods (overridable)
890     /**********************************************************
891      */

892
893     protected ObjectNode _put(String fieldName, JsonNode value)
894     {
895         _children.put(fieldName, value);
896         return this;
897     }
898 }
899