Skip to content

Adding Single Int Constructor Support (Issue #25) #121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import com.fasterxml.jackson.jr.ob.JSON;
import com.fasterxml.jackson.jr.ob.impl.ConstructorDefinition;
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;
import com.fasterxml.jackson.jr.ob.impl.POJODefinition;
Expand Down Expand Up @@ -82,31 +83,29 @@ protected POJODefinition introspectDefinition()
_findFields();
_findMethods();

Constructor<?> defaultCtor = null;
Constructor<?> stringCtor = null;
Constructor<?> longCtor = null;
ConstructorDefinition ctorDef = new ConstructorDefinition();

// A few things only matter during deserialization: constructors,
// A few things only matter during deserialization: ctorDef,
// secondary ignoral information:
if (!_forSerialization) {
for (Constructor<?> ctor : _type.getDeclaredConstructors()) {
Class<?>[] argTypes = ctor.getParameterTypes();
if (argTypes.length == 0) {
defaultCtor = ctor;
ctorDef.register(null,ctor);
} else if (argTypes.length == 1) {
Class<?> argType = argTypes[0];
if (argType == String.class) {
stringCtor = ctor;
ctorDef.register(String.class,ctor);
} else if (argType == Long.class || argType == Long.TYPE) {
longCtor = ctor;
ctorDef.register(Long.class,ctor);
} else if(argType == Integer.class || argType == Integer.TYPE) {
ctorDef.register(Integer.class,ctor);
}
}
}
}

POJODefinition def = new POJODefinition(_type,
_pruneProperties(_forSerialization),
defaultCtor, stringCtor, longCtor);
POJODefinition def = new POJODefinition(_type, _pruneProperties(_forSerialization), ctorDef);
if (_ignorableNames != null) {
def = def.withIgnorals(_ignorableNames);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ private POJODefinition _construct(Class<?> beanType, int features)
Map<String,PropBuilder> propsByName = new TreeMap<>();
_introspect(beanType, propsByName, features);

Constructor<?> defaultCtor = null;
Constructor<?> stringCtor = null;
Constructor<?> longCtor = null;
ConstructorDefinition ctorDef = new ConstructorDefinition();

for (Constructor<?> ctor : beanType.getDeclaredConstructors()) {
Class<?>[] argTypes = ctor.getParameterTypes();
if (argTypes.length == 0) {
defaultCtor = ctor;
ctorDef.register(null,ctor);
} else if (argTypes.length == 1) {
Class<?> argType = argTypes[0];
if (argType == String.class) {
stringCtor = ctor;
ctorDef.register(String.class,ctor);
} else if (argType == Long.class || argType == Long.TYPE) {
longCtor = ctor;
ctorDef.register(Long.class,ctor);
} else if (argType == Integer.class || argType == Integer.TYPE) {
ctorDef.register(Integer.class,ctor);
}
}
}
Expand All @@ -78,7 +78,7 @@ private POJODefinition _construct(Class<?> beanType, int features)
props[i++] = builder.build();
}
}
return new POJODefinition(beanType, props, defaultCtor, stringCtor, longCtor);
return new POJODefinition(beanType, props, ctorDef);
}

private static void _introspect(Class<?> currType, Map<String, PropBuilder> props,
Expand All @@ -94,8 +94,7 @@ private static void _introspect(Class<?> currType, Map<String, PropBuilder> prop
// then public fields (since 2.8); may or may not be ultimately included
// but at this point still possible
for (Field f : currType.getDeclaredFields()) {
if (!Modifier.isPublic(f.getModifiers())
|| f.isEnumConstant() || f.isSynthetic()) {
if (!Modifier.isPublic(f.getModifiers()) || f.isEnumConstant() || f.isSynthetic()) {
continue;
}
// Only include static members if (a) inclusion feature enabled and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* and serialize (write) POJO as JSON.
*/
public class BeanReader
extends ValueReader // so we can chain calls for Collections, arrays
extends ValueReader // so we can chain calls for Collections, arrays
{
protected final Map<String, BeanPropertyReader> _propsByName;

Expand All @@ -31,22 +31,17 @@ public class BeanReader
// @since 2.11
protected final Set<String> _ignorableNames;

protected final Constructor<?> _defaultCtor;
protected final Constructor<?> _stringCtor;
protected final Constructor<?> _longCtor;
ConstructorDefinition _ctorDef = new ConstructorDefinition();

/**
* Constructors used for deserialization use case
*/
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor,
Set<String> ignorableNames, Map<String, String> aliasMapping)
{
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props, ConstructorDefinition ctorDef,
Set<String> ignorableNames, Map<String, String> aliasMapping) {
super(type);
_propsByName = props;
_defaultCtor = defaultCtor;
_stringCtor = stringCtor;
_longCtor = longCtor;
_ctorDef = ctorDef;

if (ignorableNames == null) {
ignorableNames = Collections.<String>emptySet();
}
Expand All @@ -58,12 +53,13 @@ public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
}

@Deprecated // since 2.11
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor) {
this(type, props, defaultCtor, stringCtor, longCtor, null, null);
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props, ConstructorDefinition ctorDef) {
this(type, props, ctorDef, null, null);
}

public Map<String,BeanPropertyReader> propertiesByName() { return _propsByName; }
public Map<String, BeanPropertyReader> propertiesByName() {
return _propsByName;
}

public BeanPropertyReader findProperty(String name) {
BeanPropertyReader prop = _propsByName.get(name);
Expand All @@ -79,23 +75,21 @@ private final BeanPropertyReader _findAlias(String name) {
}

@Override
public Object readNext(JSONReader r, JsonParser p) throws IOException
{
public Object readNext(JSONReader r, JsonParser p) throws IOException {
JsonToken t = p.nextToken();
try {
switch (t) {
case VALUE_NULL:
return null;
case VALUE_STRING:
return create(p.getText());
case VALUE_NUMBER_INT:
return create(p.getLongValue());
case START_OBJECT:
{
case VALUE_NULL:
return null;
case VALUE_STRING:
return create(p.getText());
case VALUE_NUMBER_INT:
return create(p.getLongValue());
case START_OBJECT: {
Object bean = create();
final Object[] valueBuf = r._setterBuffer;
String propName;

for (; (propName = p.nextFieldName()) != null; ) {
BeanPropertyReader prop = findProperty(propName);
if (prop == null) {
Expand All @@ -108,45 +102,43 @@ public Object readNext(JSONReader r, JsonParser p) throws IOException
// also verify we are not confused...
if (!p.hasToken(JsonToken.END_OBJECT)) {
throw _reportProblem(p);
}
}
return bean;
}
default:
default:
}
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw JSONObjectException.from(p, "Failed to create an instance of "
+_valueType.getName()+" due to ("+e.getClass().getName()+"): "+e.getMessage(),
+ _valueType.getName() + " due to (" + e.getClass().getName() + "): " + e.getMessage(),
e);
}
throw JSONObjectException.from(p,
"Can not create a "+_valueType.getName()+" instance out of "+_tokenDesc(p));
"Can not create a " + _valueType.getName() + " instance out of " + _tokenDesc(p));
}

/**
* Method used for deserialization; will read an instance of the bean
* type using given parser.
*/
@Override
public Object read(JSONReader r, JsonParser p) throws IOException
{
public Object read(JSONReader r, JsonParser p) throws IOException {
JsonToken t = p.getCurrentToken();

try {
switch (t) {
case VALUE_NULL:
return null;
case VALUE_STRING:
return create(p.getText());
case VALUE_NUMBER_INT:
return create(p.getLongValue());
case START_OBJECT:
{
case VALUE_NULL:
return null;
case VALUE_STRING:
return create(p.getText());
case VALUE_NUMBER_INT:
return create(p.getLongValue());
case START_OBJECT: {
Object bean = create();
String propName;
final Object[] valueBuf = r._setterBuffer;

for (; (propName = p.nextFieldName()) != null; ) {
BeanPropertyReader prop = findProperty(propName);
if (prop == null) {
Expand All @@ -159,11 +151,11 @@ public Object read(JSONReader r, JsonParser p) throws IOException
// also verify we are not confused...
if (!p.hasToken(JsonToken.END_OBJECT)) {
throw _reportProblem(p);
}
}

return bean;
}
default:
default:
}
} catch (IOException e) {
throw e;
Expand All @@ -175,26 +167,25 @@ public Object read(JSONReader r, JsonParser p) throws IOException
throw JSONObjectException.from(p, "Can not create a %s instance out of %s",
_valueType.getName(), _tokenDesc(p));
}

protected Object create() throws Exception {
if (_defaultCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have default constructor to use");
}
return _defaultCtor.newInstance((Object[]) null);
return _ctorDef.generateDefault(_valueType);
}

protected Object create(String str) throws Exception {
if (_stringCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-String constructor to use");
}
return _stringCtor.newInstance(str);
return _ctorDef.generateOrThrow(String.class,str, () -> new IllegalStateException("Class " + _valueType.getName() + " does not have single-String constructor to use"));
}

protected Object create(long l) throws Exception {
if (_longCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-long constructor to use");
Constructor<Long> _longCtor = _ctorDef.getConstructor(Long.class);
Constructor<Integer> _intCtor = _ctorDef.getConstructor(Integer.class);
if (_longCtor != null) {
return _longCtor.newInstance(l);
} else if (_intCtor != null) {
return _intCtor.newInstance((int) l);
} else {
throw new IllegalStateException("Class " + _valueType.getName() + " does not have single-long or single-int constructor to use");
}
return _longCtor.newInstance(l);
}

protected void handleUnknown(JSONReader reader, JsonParser parser, String fieldName) throws IOException {
Expand All @@ -211,7 +202,7 @@ protected void handleUnknown(JSONReader reader, JsonParser parser, String fieldN
}
}
throw JSONObjectException.from(parser,
"Unrecognized JSON property \"%s\" for Bean type `%s` (known properties: [%s])",
"Unrecognized JSON property \"%s\" for Bean type `%s` (known properties: [%s])",
fieldName, _valueType.getName(), sb.toString());
}
}
Expand All @@ -220,6 +211,6 @@ protected void handleUnknown(JSONReader reader, JsonParser parser, String fieldN
}

protected IOException _reportProblem(JsonParser p) {
return JSONObjectException.from(p, "Unexpected token "+p.currentToken()+"; should get FIELD_NAME or END_OBJECT");
return JSONObjectException.from(p, "Unexpected token " + p.currentToken() + "; should get FIELD_NAME or END_OBJECT");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.fasterxml.jackson.jr.ob.impl;
Copy link
Member

@cowtowncoder cowtowncoder Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um, sorry, but this looks unnecessarily complicated: instead of Map, suppliers etc, let's just stick to plain old typed fields. Map adds overhead while not improving readability.

Same goes for exception, construction: straight-forward non-generalized construction, exception.


import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.function.Supplier;

public class ConstructorDefinition {
private final HashMap<Class<?>, Constructor<?>> constructorMap = new HashMap<>();

public ConstructorDefinition register(Class<?> firstArgClass, Constructor<?> constructor) {
constructorMap.putIfAbsent(firstArgClass, constructor);
return this;
}

public <T> Constructor<T> getConstructor(Class<T> firstArgClass) {
return (Constructor<T>) constructorMap.get(firstArgClass);
}

public <X extends Throwable, T> T generateOrThrow(Class<T> firstArgClass, Object initargs, Supplier<? extends X> exceptionSupplier)
throws X,Exception {
if (constructorMap.containsKey(firstArgClass)) return getConstructor(firstArgClass).newInstance(initargs);
else throw exceptionSupplier.get();
}

public Object generateDefault(Class<?> valueType) throws Exception {
if (constructorMap.containsKey(null)) return constructorMap.get(null).newInstance((Object[]) null);
else throw new IllegalStateException("Class " + valueType.getName() + " does not have default constructor to use");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.fasterxml.jackson.jr.ob.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
Expand All @@ -26,29 +25,19 @@ public class POJODefinition
*/
protected final Set<String> _ignorableNames;

public final Constructor<?> defaultCtor;
public final Constructor<?> stringCtor;
public final Constructor<?> longCtor;
ConstructorDefinition _ctorDef;

public POJODefinition(Class<?> type, Prop[] props,
Constructor<?> defaultCtor0, Constructor<?> stringCtor0, Constructor<?> longCtor0)
{
public POJODefinition(Class<?> type, Prop[] props, ConstructorDefinition ctorDef) {
_type = type;
_properties = props;
defaultCtor = defaultCtor0;
stringCtor = stringCtor0;
longCtor = longCtor0;
_ctorDef=ctorDef;
_ignorableNames = null;
}

protected POJODefinition(POJODefinition base,
Prop[] props, Set<String> ignorableN)
{
protected POJODefinition(POJODefinition base, Prop[] props, Set<String> ignorableN) {
_type = base._type;
_properties = props;
defaultCtor = base.defaultCtor;
stringCtor = base.stringCtor;
longCtor = base.longCtor;
_ctorDef = base._ctorDef;
_ignorableNames = ignorableN;
}

Expand Down
Loading