Jackson 3 series companion code
37 runnable examples covering the eight feature posts on ankurm.com, verified against Jackson 3.2.1 on Temurin 21.0.5. Every output committed under docs/ was produced by run-all.sh. Also documents 11 places where the published snippets do not compile or do not behave as printed against a real Jackson 3 build - most notably that writeValueAsString(List<Base>) silently drops the polymorphic type discriminator, so the post's serialised output cannot be read back.
This commit is contained in:
2
docs/output/A01FirstRoundTrip.txt
Normal file
2
docs/output/A01FirstRoundTrip.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
{"listPrice":79.99,"productId":1,"productName":"Mechanical Keyboard"}
|
||||
Mechanical Keyboard
|
||||
3
docs/output/A02SharedMapperConfiguration.txt
Normal file
3
docs/output/A02SharedMapperConfiguration.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
configured : {"invoiceId":500,"customerName":"Alice","issuedOn":"2026-04-09"}
|
||||
defaults : {"invoiceId":500,"customerName":"Alice","issuedOn":"2026-04-09","note":null}
|
||||
ObjectMapper set*() methods in Jackson 3: 0
|
||||
3
docs/output/A03ThreeProcessingModels.txt
Normal file
3
docs/output/A03ThreeProcessingModels.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
1. data binding : Order[orderId=1001, status=SHIPPED]
|
||||
2. tree model : orderId=1001 status=SHIPPED
|
||||
3. streaming : orderId=1001 status=SHIPPED
|
||||
8
docs/output/B01WriteJson.txt
Normal file
8
docs/output/B01WriteJson.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
{"articleId":1,"title":"Jackson Deep Dive","tags":["java","json"]}
|
||||
file : {"articleId":1,"title":"Jackson Deep Dive","tags":["java","json"]}
|
||||
pretty :
|
||||
{
|
||||
"articleId" : 1,
|
||||
"title" : "Jackson Deep Dive",
|
||||
"tags" : [ "java", "json" ]
|
||||
}
|
||||
3
docs/output/B02ReadJson.txt
Normal file
3
docs/output/B02ReadJson.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
from String : Jackson Deep Dive
|
||||
from File : 1
|
||||
from Stream : [java, json]
|
||||
6
docs/output/B03GenericCollections.txt
Normal file
6
docs/output/B03GenericCollections.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
size : 2
|
||||
element class : Article
|
||||
first title : First
|
||||
raw element : LinkedHashMap <- not Article
|
||||
cast fails : ClassCastException, as expected
|
||||
map value : Nine
|
||||
3
docs/output/B04PropertyOrdering.txt
Normal file
3
docs/output/B04PropertyOrdering.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
POJO : {"listPrice":79.99,"productId":1,"productName":"Mechanical Keyboard"}
|
||||
record : {"productId":1,"productName":"Mechanical Keyboard","listPrice":79.99}
|
||||
ordered : {"productId":1,"productName":"Mechanical Keyboard","listPrice":79.99}
|
||||
3
docs/output/C01RecordRoundTrip.txt
Normal file
3
docs/output/C01RecordRoundTrip.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
{"productId":101,"productName":"Wireless Keyboard","unitPrice":49.99}
|
||||
Wireless Keyboard
|
||||
round-trip equal: true
|
||||
5
docs/output/C02OptionalFields.txt
Normal file
5
docs/output/C02OptionalFields.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
present : {"customerName":"Alice","middleName":"Marie"}
|
||||
empty : {"customerName":"Bob","middleName":null}
|
||||
absent : {"customerName":"Bob"}
|
||||
isPresent: true
|
||||
missing -> Optional.empty (null? false)
|
||||
4
docs/output/C03SealedWithSubTypes.txt
Normal file
4
docs/output/C03SealedWithSubTypes.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Circle with radius: 5.0
|
||||
Rectangle 10.0 x 4.0
|
||||
lossy : [{"radius":5.0},{"width":10.0,"height":4.0}]
|
||||
correct : [{"shapeType":"circle","radius":5.0},{"shapeType":"rectangle","width":10.0,"height":4.0}]
|
||||
3
docs/output/C04SealedAutoDiscovery.txt
Normal file
3
docs/output/C04SealedAutoDiscovery.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Circle -> {"shapeType":"circle","radius":5.0} -> Circle[radius=5.0]
|
||||
Rectangle -> {"shapeType":"rectangle","width":10.0,"height":4.0} -> Rectangle[width=10.0, height=4.0]
|
||||
Triangle -> {"shapeType":"triangle","base":3.0,"height":6.0} -> Triangle[base=3.0, height=6.0]
|
||||
4
docs/output/D01RenameAndIgnore.txt
Normal file
4
docs/output/D01RenameAndIgnore.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
rename : {"order_id":1001,"customer_name":"Alice"}
|
||||
read back : OrderSummary[orderId=1001, customerName=Alice]
|
||||
ignore : {"username":"alice"}
|
||||
read back : passwordHash=null
|
||||
3
docs/output/D02InclusionAndFormat.txt
Normal file
3
docs/output/D02InclusionAndFormat.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
NON_NULL : {"productName":"Keyboard"}
|
||||
NON_EMPTY : {"productName":"Keyboard"}
|
||||
formats : {"invoiceId":500,"defaultDate":"2026-04-09","ukStyleDate":"09/04/2026","totalAmount":"199.99"}
|
||||
6
docs/output/D03AliasAndUnknownFields.txt
Normal file
6
docs/output/D03AliasAndUnknownFields.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
alias {"q":"jackson"} -> jackson
|
||||
alias {"query":"jackson"} -> jackson
|
||||
alias {"search_term":"jackson"} -> jackson
|
||||
default mapper : LenientResponse[status=OK, message=done]
|
||||
strict mapper : UnrecognizedPropertyException (as expected)
|
||||
strict + anno : OptedOutResponse[status=OK, message=done]
|
||||
5
docs/output/D04CreatorsAndUnwrapping.txt
Normal file
5
docs/output/D04CreatorsAndUnwrapping.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
creator : ImmutablePoint(x=3.5, y=7.2)
|
||||
round-trip: {"x":3.5,"y":7.2}
|
||||
unwrapped : {"street":"123 Main St","city":"Springfield","customerName":"Alice"}
|
||||
any-setter: {surprise=1, another=[true, false]}
|
||||
any-getter: {"knownField":"a","surprise":1,"another":[true,false]}
|
||||
5
docs/output/E03SimpleModuleRegistration.txt
Normal file
5
docs/output/E03SimpleModuleRegistration.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
serialised : {"amount":20.00,"currency":"USD"}
|
||||
amount : 20.0 (scale lost)
|
||||
amount exact : 20.00 (scale preserved)
|
||||
missing field: Money[amount=0, currencyCode=EUR]
|
||||
no module : {"amount":19.999,"currencyCode":"usd"}
|
||||
2
docs/output/E04MixinAnnotations.txt
Normal file
2
docs/output/E04MixinAnnotations.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
with mixin : {"city":"Springfield","street":"123 Main St","zip":"12345"}
|
||||
without mixin: {"city":"Springfield","internalTrackingCode":"INTERNAL-X99","postalCode":"12345","street":"123 Main St"}
|
||||
3
docs/output/E05ValueSerializerDirect.txt
Normal file
3
docs/output/E05ValueSerializerDirect.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
custom : {"assignee":"u-42","title":"Fix build"}
|
||||
default : {"assignee":{"value":"u-42"},"title":"Fix build"}
|
||||
base class: tools.jackson.databind.ValueSerializer
|
||||
20
docs/output/F01SerialiseMixedList.txt
Normal file
20
docs/output/F01SerialiseMixedList.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
--- 1. single element: discriminator present ---
|
||||
{"paymentType":"credit_card","amountDue":99.99,"cardNetwork":"VISA","cardNumberLastFour":"4242","paymentId":1}
|
||||
--- 2. BROKEN: writeValueAsString(List) drops paymentType ---
|
||||
[{"amountDue":99.99,"cardNetwork":"VISA","cardNumberLastFour":"4242","paymentId":1},{"amountDue":250.0,"bankAccountIban":"GB29NWBK60161331926819","bankName":"National Bank","paymentId":2}]
|
||||
--- 3. FIX A: writerFor(TypeReference) ---
|
||||
[ {
|
||||
"paymentType" : "credit_card",
|
||||
"amountDue" : 99.99,
|
||||
"cardNetwork" : "VISA",
|
||||
"cardNumberLastFour" : "4242",
|
||||
"paymentId" : 1
|
||||
}, {
|
||||
"paymentType" : "bank_transfer",
|
||||
"amountDue" : 250.0,
|
||||
"bankAccountIban" : "GB29NWBK60161331926819",
|
||||
"bankName" : "National Bank",
|
||||
"paymentId" : 2
|
||||
} ]
|
||||
--- 4. FIX B: a typed array carries its component type ---
|
||||
[{"paymentType":"credit_card","amountDue":99.99,"cardNetwork":"VISA","cardNumberLastFour":"4242","paymentId":1},{"paymentType":"bank_transfer","amountDue":250.0,"bankAccountIban":"GB29NWBK60161331926819","bankName":"National Bank","paymentId":2}]
|
||||
4
docs/output/F02DeserialiseMixedList.txt
Normal file
4
docs/output/F02DeserialiseMixedList.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Card ending: 4242
|
||||
Bank: National Bank
|
||||
lossy JSON round-trip -> InvalidTypeIdException
|
||||
correct JSON round-trip -> 2 payments, CreditCardPayment first
|
||||
7
docs/output/F03IncludeStrategies.txt
Normal file
7
docs/output/F03IncludeStrategies.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
PROPERTY : {"kind":"card","amountDue":99.0}
|
||||
WRAPPER_OBJECT : {"card":{"amountDue":99.0}}
|
||||
WRAPPER_ARRAY : ["card",{"amountDue":99.0}]
|
||||
EXISTING_PROPERTY : {"kind":"card","amountDue":99.0}
|
||||
read PROPERTY -> PropCard[amountDue=99.0]
|
||||
read WRAPPER_OBJECT -> WrapObjCard[amountDue=99.0]
|
||||
read WRAPPER_ARRAY -> WrapArrCard[amountDue=99.0]
|
||||
3
docs/output/F04UnknownTypeId.txt
Normal file
3
docs/output/F04UnknownTypeId.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
unknown logical name -> InvalidTypeIdException
|
||||
attacker-supplied class name -> InvalidTypeIdException
|
||||
missing discriminator -> InvalidTypeIdException
|
||||
2
docs/output/G01StreamingParserFilter.txt
Normal file
2
docs/output/G01StreamingParserFilter.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
ERROR: Database connection failed
|
||||
Total errors found: 1
|
||||
4
docs/output/G02StreamingGenerator.txt
Normal file
4
docs/output/G02StreamingGenerator.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
records written : 1000000
|
||||
file size : 30 MB
|
||||
elapsed : 129 ms
|
||||
heap delta : 0 MB <- the document is never held in memory
|
||||
8
docs/output/G03TreeModelNavigation.txt
Normal file
8
docs/output/G03TreeModelNavigation.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Customer: Alice
|
||||
KB-01 x2
|
||||
MS-42 x1
|
||||
Has discount: false
|
||||
path(missing) : (class MissingNode)
|
||||
get(missing) : null
|
||||
deep path : '<default>'
|
||||
treeToValue : CustomerRecord[name=Alice, tier=gold]
|
||||
5
docs/output/G04ThreeApproachesMeasured.txt
Normal file
5
docs/output/G04ThreeApproachesMeasured.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
input file : 20 MB, 200000 entries
|
||||
|
||||
data binding (readValue) errors=4000 524 ms heap delta 47 MB
|
||||
tree model (readTree) errors=4000 341 ms heap delta 102 MB
|
||||
streaming (JsonParser) errors=4000 78 ms heap delta 1 MB
|
||||
3
docs/output/H01SafePolymorphismByAnnotation.txt
Normal file
3
docs/output/H01SafePolymorphismByAnnotation.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
email : EmailNotification[a@example.com]
|
||||
sms : SmsNotification[+441234567890]
|
||||
attack: rejected with InvalidTypeIdException
|
||||
14
docs/output/H02DefaultTypingRemoved.txt
Normal file
14
docs/output/H02DefaultTypingRemoved.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
--- tools.jackson.databind.ObjectMapper ---
|
||||
enableDefaultTyping ABSENT
|
||||
activateDefaultTyping ABSENT
|
||||
setSerializationInclusion ABSENT
|
||||
registerModule ABSENT
|
||||
addMixIn ABSENT
|
||||
total set*() mutators: 0
|
||||
|
||||
--- tools.jackson.databind.json.JsonMapper.Builder ---
|
||||
activateDefaultTyping present
|
||||
deactivateDefaultTyping present
|
||||
polymorphicTypeValidator present
|
||||
changeDefaultPropertyInclusion present
|
||||
serializationInclusion ABSENT
|
||||
3
docs/output/H03PolymorphicTypeValidatorAllowlist.txt
Normal file
3
docs/output/H03PolymorphicTypeValidatorAllowlist.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
allowed written : {"@class":"com.ankurm.jackson3.part7security.H03PolymorphicTypeValidatorAllowlist$Envelope","body":{"@class":"com.ankurm.jackson3.part7security.H03PolymorphicTypeValidatorAllowlist$SafePayload","note":"ok"}}
|
||||
allowed read : SafePayload[ok]
|
||||
rogue : rejected with InvalidTypeIdException
|
||||
11
docs/output/H04StreamReadConstraints.txt
Normal file
11
docs/output/H04StreamReadConstraints.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
--- Jackson 3 defaults ---
|
||||
max nesting depth : 500
|
||||
max number length : 1000
|
||||
max string length : 100000000
|
||||
max name length : 50000
|
||||
max doc length : -1 (-1 = unlimited)
|
||||
|
||||
1200-deep nesting, default limits -> rejected (StreamConstraintsException)
|
||||
20-deep nesting, strict limits -> rejected (StreamConstraintsException)
|
||||
5-deep nesting, strict limits -> accepted
|
||||
3KB string, strict limits -> rejected (StreamConstraintsException)
|
||||
4
docs/output/H05NeverDeserialiseIntoObject.txt
Normal file
4
docs/output/H05NeverDeserialiseIntoObject.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
as Object : {action=ship, quantity=3, extra={nested=[1, 2]}}
|
||||
runtime type: java.util.LinkedHashMap <- a plain Map, no arbitrary class was instantiated
|
||||
as DTO : MyRequestDto[action=ship, quantity=3]
|
||||
bad input : rejected with InvalidFormatException
|
||||
9
docs/output/Y01UncheckedExceptions.txt
Normal file
9
docs/output/Y01UncheckedExceptions.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
JacksonException extends RuntimeException : true
|
||||
JacksonException extends IOException : false
|
||||
|
||||
-- catch (IOException) around I/O + Jackson --
|
||||
ESCAPED the IOException handler -> StreamReadException
|
||||
-- catch (JacksonException) then catch (IOException) --
|
||||
caught: StreamReadException
|
||||
-- unchecked exceptions inside a stream --
|
||||
[{"orderId":1,"customerName":"Alice"}, {"orderId":2,"customerName":"Bob"}]
|
||||
6
docs/output/Y02TrailingTokens.txt
Normal file
6
docs/output/Y02TrailingTokens.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
FAIL_ON_TRAILING_TOKENS default : true
|
||||
|
||||
Jackson 3 default -> rejected: MismatchedInputException
|
||||
2.x behaviour -> accepted: OrderDto[orderId=1]
|
||||
garbage, default -> rejected: StreamReadException
|
||||
garbage, relaxed -> accepted: OrderDto[orderId=1]
|
||||
8
docs/output/Y03DateTimeDefaults.txt
Normal file
8
docs/output/Y03DateTimeDefaults.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
SerializationFeature has WRITE_DATES_AS_TIMESTAMPS : false
|
||||
DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS default : false
|
||||
|
||||
defaults (ISO-8601):
|
||||
{"day":"2026-09-15","startsAt":"2026-09-15T10:30:00","recordedAt":"2026-09-15T10:30:00Z","zoned":"2026-09-15T10:30:00Z","length":"PT45M"}
|
||||
with WRITE_DATES_AS_TIMESTAMPS enabled:
|
||||
{"day":[2026,9,15],"startsAt":[2026,9,15,10,30],"recordedAt":1789468200.000000000,"zoned":1789468200.000000000,"length":"PT45M"}
|
||||
numeric form reads back: 2026-09-15
|
||||
5
docs/output/Y04ImmutableMapperAndReaders.txt
Normal file
5
docs/output/Y04ImmutableMapperAndReaders.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
shared : {"firstName":"Ada","lastName":"Lovelace","middleName":null}
|
||||
rebuilt snake_case : {"first_name":"Ada","last_name":"Lovelace"}
|
||||
shared unchanged : {"firstName":"Ada","lastName":"Lovelace","middleName":null}
|
||||
writer view pretty : { "firstName" : "Ada", "lastName" : "Lovelace", "middleName" : null }
|
||||
reader view strict : rejected (UnrecognizedPropertyException) without touching the shared mapper
|
||||
6
docs/output/Y05CreatorDetection.txt
Normal file
6
docs/output/Y05CreatorDetection.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
MapperFeature.AUTO_DETECT_CREATORS exists : false
|
||||
Nearest surviving features : [INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES, DETECT_PARAMETER_NAMES, SORT_CREATOR_PROPERTIES_FIRST]
|
||||
|
||||
implicit single-arg ctor : ImplicitOrderId[ord-1]
|
||||
explicit @JsonCreator : ExplicitOrderId[ord-2]
|
||||
round-trip via @JsonValue: "ord-3"
|
||||
13
docs/output/Y06RecyclerPoolTuning.txt
Normal file
13
docs/output/Y06RecyclerPoolTuning.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
default pool : ConcurrentDequePool
|
||||
cores : 2
|
||||
|
||||
--- 1 thread(s), 40000 round-trips each ---
|
||||
threadLocalPool (2.x default) 301 ms
|
||||
concurrentDeque (3.x default) 310 ms
|
||||
nonRecyclingPool (no reuse) 496 ms
|
||||
|
||||
--- 8 thread(s), 40000 round-trips each ---
|
||||
threadLocalPool (2.x default) 451 ms
|
||||
concurrentDeque (3.x default) 450 ms
|
||||
nonRecyclingPool (no reuse) 808 ms
|
||||
|
||||
Reference in New Issue
Block a user