Skip to content

Commit 267abe1

Browse files
committed
Baseline work for testing, eventually resolving #1425; now produces better error messages.
1 parent cf88e7b commit 267abe1

File tree

6 files changed

+162
-253
lines changed

6 files changed

+162
-253
lines changed

src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ public Object handleMissingInstantiator(Class<?> instClass, ValueInstantiator va
991991
}
992992
reportBadDefinition(constructType(instClass), String.format(
993993
"DeserializationProblemHandler.handleMissingInstantiator() for type %s returned value of type %s",
994-
instClass, instance.getClass()));
994+
instClass, ClassUtil.classNameOf(instance)));
995995
}
996996
h = h.next();
997997
}
@@ -1001,11 +1001,11 @@ public Object handleMissingInstantiator(Class<?> instClass, ValueInstantiator va
10011001
// match with token.
10021002
if ((valueInst != null) && !valueInst.canInstantiate()) {
10031003
msg = String.format("Can not construct instance of %s (no Creators, like default construct, exist): %s",
1004-
instClass.getName(), msg);
1004+
ClassUtil.nameOf(instClass), msg);
10051005
return reportBadDefinition(constructType(instClass), msg);
10061006
}
10071007
msg = String.format("Can not construct instance of %s (although at least one Creator exists): %s",
1008-
instClass.getName(), msg);
1008+
ClassUtil.nameOf(instClass), msg);
10091009
return reportInputMismatch(instClass, msg);
10101010
}
10111011

@@ -1232,7 +1232,7 @@ public <T> T reportUnresolvedObjectId(ObjectIdReader oidReader, Object bean)
12321232
throws JsonMappingException
12331233
{
12341234
String msg = String.format("No Object Id found for an instance of %s, to assign to property '%s'",
1235-
bean.getClass().getName(), oidReader.propertyName);
1235+
ClassUtil.classNameOf(bean), oidReader.propertyName);
12361236
return reportInputMismatch(oidReader.idProperty, msg);
12371237
}
12381238

@@ -1452,7 +1452,7 @@ public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue
14521452
String msg) {
14531453
return InvalidFormatException.from(_parser,
14541454
String.format("Can not deserialize Map key of type %s from String %s: %s",
1455-
keyClass.getName(), _quotedString(keyValue), msg),
1455+
ClassUtil.nameOf(keyClass), _quotedString(keyValue), msg),
14561456
keyValue, keyClass);
14571457
}
14581458

@@ -1473,7 +1473,7 @@ public JsonMappingException weirdStringException(String value, Class<?> instClas
14731473
String msg) {
14741474
return InvalidFormatException.from(_parser,
14751475
String.format("Can not deserialize value of type %s from String %s: %s",
1476-
instClass.getName(), _quotedString(value), msg),
1476+
ClassUtil.nameOf(instClass), _quotedString(value), msg),
14771477
value, instClass);
14781478
}
14791479

@@ -1488,7 +1488,7 @@ public JsonMappingException weirdNumberException(Number value, Class<?> instClas
14881488
String msg) {
14891489
return InvalidFormatException.from(_parser,
14901490
String.format("Can not deserialize value of type %s from number %s: %s",
1491-
instClass.getName(), String.valueOf(value), msg),
1491+
ClassUtil.nameOf(instClass), String.valueOf(value), msg),
14921492
value, instClass);
14931493
}
14941494

@@ -1505,7 +1505,7 @@ public JsonMappingException instantiationException(Class<?> instClass, Throwable
15051505
// Most likely problem with Creator definition, right?
15061506
JavaType type = constructType(instClass);
15071507
String msg = String.format("Can not construct instance of %s, problem: %s",
1508-
instClass.getName(), cause.getMessage());
1508+
ClassUtil.nameOf(instClass), cause.getMessage());
15091509
InvalidDefinitionException e = InvalidDefinitionException.from(_parser, msg, type);
15101510
e.initCause(cause);
15111511
return e;
@@ -1523,7 +1523,8 @@ public JsonMappingException instantiationException(Class<?> instClass, Throwable
15231523
public JsonMappingException instantiationException(Class<?> instClass, String msg0) {
15241524
// Most likely problem with Creator definition, right?
15251525
JavaType type = constructType(instClass);
1526-
String msg = String.format("Can not construct instance of %s: %s", instClass.getName(), msg0);
1526+
String msg = String.format("Can not construct instance of %s: %s",
1527+
ClassUtil.nameOf(instClass), msg0);
15271528
return InvalidDefinitionException.from(_parser, msg, type);
15281529
}
15291530

src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,19 @@ public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
356356

357357
// Most likely case: base64 encoded String?
358358
if (t == JsonToken.VALUE_STRING) {
359-
return p.getBinaryValue(ctxt.getBase64Variant());
359+
try {
360+
return p.getBinaryValue(ctxt.getBase64Variant());
361+
} catch (JsonParseException e) {
362+
// 25-Nov-2016, tatu: related to [databind#1425], try to convert
363+
// to a more usable one, as it's not really a JSON-level parse
364+
// exception, but rather binding from JSON String into base64 decoded
365+
// binary data
366+
String msg = e.getOriginalMessage();
367+
if (msg.contains("base64")) {
368+
return (byte[]) ctxt.handleWeirdStringValue(byte[].class,
369+
p.getText(), msg);
370+
}
371+
}
360372
}
361373
// 31-Dec-2009, tatu: Also may be hidden as embedded Object
362374
if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {

src/main/java/com/fasterxml/jackson/databind/node/TextNode.java

+16-131
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.fasterxml.jackson.core.io.NumberInput;
88
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
99
import com.fasterxml.jackson.databind.SerializerProvider;
10+
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
11+
import com.fasterxml.jackson.databind.util.ClassUtil;
1012

1113
/**
1214
* Value node that contains a text value.
@@ -60,93 +62,16 @@ public String textValue() {
6062
@SuppressWarnings("resource")
6163
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
6264
{
63-
ByteArrayBuilder builder = new ByteArrayBuilder(100);
64-
final String str = _value;
65-
int ptr = 0;
66-
int len = str.length();
67-
68-
main_loop:
69-
while (ptr < len) {
70-
// first, we'll skip preceding white space, if any
71-
char ch;
72-
do {
73-
ch = str.charAt(ptr++);
74-
if (ptr >= len) {
75-
break main_loop;
76-
}
77-
} while (ch <= ' ');
78-
int bits = b64variant.decodeBase64Char(ch);
79-
if (bits < 0) {
80-
_reportInvalidBase64(b64variant, ch, 0);
81-
}
82-
int decodedData = bits;
83-
// then second base64 char; can't get padding yet, nor ws
84-
if (ptr >= len) {
85-
_reportBase64EOF();
86-
}
87-
ch = str.charAt(ptr++);
88-
bits = b64variant.decodeBase64Char(ch);
89-
if (bits < 0) {
90-
_reportInvalidBase64(b64variant, ch, 1);
91-
}
92-
decodedData = (decodedData << 6) | bits;
93-
// third base64 char; can be padding, but not ws
94-
if (ptr >= len) {
95-
// but as per [JACKSON-631] can be end-of-input, iff not using padding
96-
if (!b64variant.usesPadding()) {
97-
// Got 12 bits, only need 8, need to shift
98-
decodedData >>= 4;
99-
builder.append(decodedData);
100-
break;
101-
}
102-
_reportBase64EOF();
103-
}
104-
ch = str.charAt(ptr++);
105-
bits = b64variant.decodeBase64Char(ch);
106-
107-
// First branch: can get padding (-> 1 byte)
108-
if (bits < 0) {
109-
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
110-
_reportInvalidBase64(b64variant, ch, 2);
111-
}
112-
// Ok, must get padding
113-
if (ptr >= len) {
114-
_reportBase64EOF();
115-
}
116-
ch = str.charAt(ptr++);
117-
if (!b64variant.usesPaddingChar(ch)) {
118-
_reportInvalidBase64(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
119-
}
120-
// Got 12 bits, only need 8, need to shift
121-
decodedData >>= 4;
122-
builder.append(decodedData);
123-
continue;
124-
}
125-
// Nope, 2 or 3 bytes
126-
decodedData = (decodedData << 6) | bits;
127-
// fourth and last base64 char; can be padding, but not ws
128-
if (ptr >= len) {
129-
// but as per [JACKSON-631] can be end-of-input, iff not using padding
130-
if (!b64variant.usesPadding()) {
131-
decodedData >>= 2;
132-
builder.appendTwoBytes(decodedData);
133-
break;
134-
}
135-
_reportBase64EOF();
136-
}
137-
ch = str.charAt(ptr++);
138-
bits = b64variant.decodeBase64Char(ch);
139-
if (bits < 0) {
140-
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
141-
_reportInvalidBase64(b64variant, ch, 3);
142-
}
143-
decodedData >>= 2;
144-
builder.appendTwoBytes(decodedData);
145-
} else {
146-
// otherwise, our triple is now complete
147-
decodedData = (decodedData << 6) | bits;
148-
builder.appendThreeBytes(decodedData);
149-
}
65+
final String str = _value.trim();
66+
ByteArrayBuilder builder = new ByteArrayBuilder(4 + ((str.length() * 3) << 2));
67+
try {
68+
b64variant.decode(str, builder);
69+
} catch (IllegalArgumentException e) {
70+
throw InvalidFormatException.from(null,
71+
String.format(
72+
"Can not access contents of TextNode as binary due to broken Base64 encoding: %s",
73+
e.getMessage()),
74+
str, byte[].class);
15075
}
15176
return builder.toByteArray();
15277
}
@@ -155,7 +80,7 @@ public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
15580
public byte[] binaryValue() throws IOException {
15681
return getBinaryValue(Base64Variants.getDefaultVariant());
15782
}
158-
83+
15984
/*
16085
/**********************************************************
16186
/* General type coercions
@@ -210,12 +135,12 @@ public double asDouble(double defaultValue) {
210135
*/
211136

212137
@Override
213-
public final void serialize(JsonGenerator jg, SerializerProvider provider) throws IOException
138+
public final void serialize(JsonGenerator g, SerializerProvider provider) throws IOException
214139
{
215140
if (_value == null) {
216-
jg.writeNull();
141+
g.writeNull();
217142
} else {
218-
jg.writeString(_value);
143+
g.writeString(_value);
219144
}
220145
}
221146

@@ -258,44 +183,4 @@ protected static void appendQuoted(StringBuilder sb, String content)
258183
CharTypes.appendQuoted(sb, content);
259184
sb.append('"');
260185
}
261-
262-
/*
263-
/**********************************************************
264-
/* Helper methods
265-
/**********************************************************
266-
*/
267-
268-
protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex)
269-
throws JsonParseException
270-
{
271-
_reportInvalidBase64(b64variant, ch, bindex, null);
272-
}
273-
274-
/**
275-
* @param bindex Relative index within base64 character unit; between 0
276-
* and 3 (as unit has exactly 4 characters)
277-
*/
278-
protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg)
279-
throws JsonParseException
280-
{
281-
String base;
282-
if (ch <= ' ') {
283-
base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
284-
} else if (b64variant.usesPaddingChar(ch)) {
285-
base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
286-
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
287-
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
288-
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
289-
} else {
290-
base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
291-
}
292-
if (msg != null) {
293-
base = base + ": " + msg;
294-
}
295-
throw new JsonParseException(null, base, JsonLocation.NA);
296-
}
297-
298-
protected void _reportBase64EOF() throws JsonParseException {
299-
throw new JsonParseException(null, "Unexpected end-of-String when base64 content");
300-
}
301186
}

src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java

+6
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,12 @@ public static String nameOf(Class<?> cls) {
715715
if (cls == null) {
716716
return "[null]";
717717
}
718+
if (cls.isArray()) {
719+
return nameOf(cls.getComponentType())+"[]";
720+
}
721+
if (cls.isPrimitive()) {
722+
cls.getSimpleName();
723+
}
718724
return cls.getName();
719725
}
720726

src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java

+5
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ protected void assertEquals(int[] exp, int[] act)
243243
assertArrayEquals(exp, act);
244244
}
245245

246+
protected void assertEquals(byte[] exp, byte[] act)
247+
{
248+
assertArrayEquals(exp, act);
249+
}
250+
246251
/**
247252
* Helper method for verifying 3 basic cookie cutter cases;
248253
* identity comparison (true), and against null (false),

0 commit comments

Comments
 (0)