@@ -60,7 +60,21 @@ public enum Feature implements FormatFeature
6060 *
6161 * @since 2.20
6262 */
63- READ_UNDEFINED_AS_EMBEDDED_OBJECT (false )
63+ READ_UNDEFINED_AS_EMBEDDED_OBJECT (false ),
64+
65+ /**
66+ * Feature that determines how a CBOR "simple value" of major type 7 is exposed by parser.
67+ * <p>
68+ * When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with
69+ * an embedded value of type {@link CBORSimpleValue}, allowing the caller to distinguish
70+ * these values from actual {@link JsonToken#VALUE_NUMBER_INT}s.
71+ * When disabled, simple values are returned as {@link JsonToken#VALUE_NUMBER_INT}.
72+ *<p>
73+ * The default value is {@code false} for backwards compatibility (with versions prior to 2.20).
74+ *
75+ * @since 2.20
76+ */
77+ READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT (false )
6478 ;
6579
6680 private final boolean _defaultState ;
@@ -363,6 +377,14 @@ public int getFirstTag() {
363377 */
364378 protected TagList _tagValues = new TagList ();
365379
380+ /**
381+ * When major type 7 value is encountered and exposed as {@link JsonToken#VALUE_EMBEDDED_OBJECT},
382+ * the value will be stored here.
383+ *
384+ * @since 2.20
385+ */
386+ protected CBORSimpleValue _simpleValue ;
387+
366388 /**
367389 * Flag that indicates that the current token has not yet
368390 * been fully processed, and needs to be finished for
@@ -824,9 +846,9 @@ public JsonToken nextToken() throws IOException
824846 _skipIncomplete ();
825847 }
826848 _tokenInputTotal = _currInputProcessed + _inputPtr ;
827- // also: clear any data retained so far
828- _numTypesValid = NR_UNKNOWN ;
829- _binaryValue = null ;
849+
850+ // also: clear any data retained for previous token
851+ clearRetainedValues () ;
830852
831853 // First: need to keep track of lengths of defined-length Arrays and
832854 // Objects (to materialize END_ARRAY/END_OBJECT as necessary);
@@ -1453,12 +1475,12 @@ public boolean nextFieldName(SerializableString str) throws IOException
14531475 {
14541476 // Two parsing modes; can only succeed if expecting field name, so handle that first:
14551477 if (_streamReadContext .inObject () && _currToken != JsonToken .FIELD_NAME ) {
1456- _numTypesValid = NR_UNKNOWN ;
14571478 if (_tokenIncomplete ) {
14581479 _skipIncomplete ();
14591480 }
14601481 _tokenInputTotal = _currInputProcessed + _inputPtr ;
1461- _binaryValue = null ;
1482+ // need to clear retained values for previous token
1483+ clearRetainedValues ();
14621484 _tagValues .clear ();
14631485 // completed the whole Object?
14641486 if (!_streamReadContext .expectMoreValues ()) {
@@ -1506,19 +1528,19 @@ public boolean nextFieldName(SerializableString str) throws IOException
15061528 }
15071529 }
15081530 // otherwise just fall back to default handling; should occur rarely
1509- return (nextToken () == JsonToken .FIELD_NAME ) && str .getValue ().equals (getCurrentName ());
1531+ return (nextToken () == JsonToken .FIELD_NAME ) && str .getValue ().equals (currentName ());
15101532 }
15111533
15121534 @ Override
15131535 public String nextFieldName () throws IOException
15141536 {
15151537 if (_streamReadContext .inObject () && _currToken != JsonToken .FIELD_NAME ) {
1516- _numTypesValid = NR_UNKNOWN ;
15171538 if (_tokenIncomplete ) {
15181539 _skipIncomplete ();
15191540 }
15201541 _tokenInputTotal = _currInputProcessed + _inputPtr ;
1521- _binaryValue = null ;
1542+ // need to clear retained values for previous token
1543+ clearRetainedValues ();
15221544 _tagValues .clear ();
15231545 // completed the whole Object?
15241546 if (!_streamReadContext .expectMoreValues ()) {
@@ -1843,7 +1865,10 @@ public Object getEmbeddedObject() throws IOException
18431865 if (_tokenIncomplete ) {
18441866 _finishToken ();
18451867 }
1846- if (_currToken == JsonToken .VALUE_EMBEDDED_OBJECT ) {
1868+ if (_currToken == JsonToken .VALUE_EMBEDDED_OBJECT ) {
1869+ if (_simpleValue != null ) {
1870+ return _simpleValue ;
1871+ }
18471872 return _binaryValue ;
18481873 }
18491874 return null ;
@@ -1933,11 +1958,11 @@ private final byte[] _getBinaryFromString(Base64Variant variant) throws IOExcept
19331958 /**
19341959 * Checking whether the current token represents an `undefined` value (0xF7).
19351960 * <p>
1936- * This method allows distinguishing between real {@code null} and ` undefined` ,
1961+ * This method allows distinguishing between real {@code null} and {@code undefined} ,
19371962 * even if {@link CBORParser.Feature#READ_UNDEFINED_AS_EMBEDDED_OBJECT} is disabled
19381963 * and the token is reported as {@link JsonToken#VALUE_NULL}.
19391964 *
1940- * @return {@code true} if current token is an ` undefined` , {@code false} otherwise
1965+ * @return {@code true} if current token is an {@code undefined} , {@code false} otherwise
19411966 *
19421967 * @since 2.20
19431968 */
@@ -3713,38 +3738,50 @@ protected JsonToken _decodeUndefinedValue() {
37133738 * Helper method that deals with details of decoding unallocated "simple values"
37143739 * and exposing them as expected token.
37153740 * <p>
3716- * As of Jackson 2.12, simple values are exposed as
3717- * {@link JsonToken#VALUE_NUMBER_INT}s,
3718- * but in later versions this is planned to be changed to separate value type.
3741+ * Starting with Jackson 2.20, this behavior can be changed by enabling the
3742+ * {@link CBORParser.Feature#READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT}
3743+ * feature, in which case simple values are returned as {@link JsonToken#VALUE_EMBEDDED_OBJECT} with an
3744+ * embedded {@link CBORSimpleValue} instance.
37193745 *
37203746 * @since 2.12
37213747 */
37223748 public JsonToken _decodeSimpleValue (int lowBits , int ch ) throws IOException {
37233749 if (lowBits > 24 ) {
37243750 _invalidToken (ch );
37253751 }
3752+ final boolean simpleAsEmbedded = Feature .READ_SIMPLE_VALUE_AS_EMBEDDED_OBJECT .enabledIn (_formatFeatures );
37263753 if (lowBits < 24 ) {
3727- _numberInt = lowBits ;
3754+ if (simpleAsEmbedded ) {
3755+ _simpleValue = new CBORSimpleValue (lowBits );
3756+ } else {
3757+ _numberInt = lowBits ;
3758+ }
37283759 } else { // need another byte
37293760 if (_inputPtr >= _inputEnd ) {
37303761 loadMoreGuaranteed ();
37313762 }
3732- _numberInt = _inputBuffer [ _inputPtr ++] & 0xFF ;
3763+
37333764 // As per CBOR spec, values below 32 not allowed to avoid
37343765 // confusion (as well as guarantee uniqueness of encoding)
3735- if (_numberInt < 32 ) {
3766+ int value = _inputBuffer [_inputPtr ++] & 0xFF ;
3767+ if (value < 32 ) {
37363768 throw _constructError ("Invalid second byte for simple value: 0x"
3737- +Integer .toHexString (_numberInt )+" (only values 0x20 - 0xFF allowed)" );
3769+ +Integer .toHexString (value )+" (only values 0x20 - 0xFF allowed)" );
3770+ }
3771+
3772+ if (simpleAsEmbedded ) {
3773+ _simpleValue = new CBORSimpleValue (value );
3774+ } else {
3775+ _numberInt = value ;
37383776 }
37393777 }
37403778
3741- // 25-Nov-2020, tatu: Although ideally we should report these
3742- // as `JsonToken.VALUE_EMBEDDED_OBJECT`, due to late addition
3743- // of handling in 2.12, simple value in 2.12 will be reported
3744- // as simple ints.
3779+ if (simpleAsEmbedded ) {
3780+ return JsonToken .VALUE_EMBEDDED_OBJECT ;
3781+ }
37453782
37463783 _numTypesValid = NR_INT ;
3747- return ( JsonToken .VALUE_NUMBER_INT ) ;
3784+ return JsonToken .VALUE_NUMBER_INT ;
37483785 }
37493786
37503787 /*
@@ -4101,4 +4138,11 @@ private void createChildObjectContext(final int len) throws IOException {
41014138 _streamReadContext = _streamReadContext .createChildObjectContext (len );
41024139 _streamReadConstraints .validateNestingDepth (_streamReadContext .getNestingDepth ());
41034140 }
4141+
4142+ // @since 2.20
4143+ private void clearRetainedValues () {
4144+ _numTypesValid = NR_UNKNOWN ;
4145+ _binaryValue = null ;
4146+ _simpleValue = null ;
4147+ }
41044148}
0 commit comments