1
0

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:
2026-08-04 23:12:11 +05:30
commit c438afc33b
94 changed files with 3387 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
{"listPrice":79.99,"productId":1,"productName":"Mechanical Keyboard"}
Mechanical Keyboard

View 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

View 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

View 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" ]
}

View File

@@ -0,0 +1,3 @@
from String : Jackson Deep Dive
from File : 1
from Stream : [java, json]

View 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

View 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}

View File

@@ -0,0 +1,3 @@
{"productId":101,"productName":"Wireless Keyboard","unitPrice":49.99}
Wireless Keyboard
round-trip equal: true

View 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)

View 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}]

View 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]

View 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

View 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"}

View 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]

View 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]}

View 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"}

View 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"}

View 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

View 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}]

View File

@@ -0,0 +1,4 @@
Card ending: 4242
Bank: National Bank
lossy JSON round-trip -> InvalidTypeIdException
correct JSON round-trip -> 2 payments, CreditCardPayment first

View 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]

View File

@@ -0,0 +1,3 @@
unknown logical name -> InvalidTypeIdException
attacker-supplied class name -> InvalidTypeIdException
missing discriminator -> InvalidTypeIdException

View File

@@ -0,0 +1,2 @@
ERROR: Database connection failed
Total errors found: 1

View 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

View 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]

View 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

View File

@@ -0,0 +1,3 @@
email : EmailNotification[a@example.com]
sms : SmsNotification[+441234567890]
attack: rejected with InvalidTypeIdException

View 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

View 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

View 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)

View 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

View 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"}]

View 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]

View 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

View 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

View 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"

View 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