Skip to content

Commit

Permalink
Partial implementation for #1556, still need to consider what to do w…
Browse files Browse the repository at this point in the history
…ith arrays
  • Loading branch information
cowtowncoder committed Mar 17, 2017
1 parent 2efcf08 commit 8f8904a
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 16 deletions.
71 changes: 66 additions & 5 deletions src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3596,7 +3596,7 @@ public <T> T convertValue(Object fromValue, Class<T> toValueType)
public <T> T convertValue(Object fromValue, TypeReference<?> toValueTypeRef)
throws IllegalArgumentException
{
return (T) convertValue(fromValue, _typeFactory.constructType(toValueTypeRef));
return (T) _convert(fromValue, _typeFactory.constructType(toValueTypeRef));
}

/**
Expand Down Expand Up @@ -3625,10 +3625,9 @@ protected Object _convert(Object fromValue, JavaType toValueType)
// This defaults primitives and fires deserializer getNullValue hooks.
if (fromValue != null) {
// also, as per [databind#11], consider case for simple cast
/* But with caveats: one is that while everything is Object.class, we don't
* want to "optimize" that out; and the other is that we also do not want
* to lose conversions of generic types.
*/
// But with caveats: one is that while everything is Object.class, we don't
// want to "optimize" that out; and the other is that we also do not want
// to lose conversions of generic types.
Class<?> targetType = toValueType.getRawClass();
if (targetType != Object.class
&& !toValueType.hasGenericTypes()
Expand Down Expand Up @@ -3673,6 +3672,68 @@ protected Object _convert(Object fromValue, JavaType toValueType)
}
}

/**
* Convenience method similar to {@link #convertValue(Object, JavaType)} but one
* in which an existing value (`valueToUpdate`) is modified based on contents
* of serialization of another object (`updateWithValue`).
*<p>
* Implementation is approximately as follows:
*<ol>
* <li>Serialize `updateWithValue` into {@link TokenBuffer}</li>
* <li>Construct {@link ObjectReader} with `valueToUpdate` (using {@link #readerForUpdating(Object)})
* </li>
* <li>Construct {@link JsonParser} (using {@link TokenBuffer#asParser()})
* </li>
* <li>Update using {@link ObjectReader#readValue(JsonParser)}.
* </li>
* <li>Return `valueToUpdate`
* </li>
*</ol>
*<p>
* Note that update is "shallow" in that only first level of properties (or, immediate contents
* of container to update) are modified, unless properties themselves indicate that
* merging should be applied for contents. Such merging can be specified using
* annotations (see <code>JsonMerge</code>) as well as using "config overrides" (see
* {@link #configOverride(Class)} and {@link #setDefaultMergeable(Boolean)}).
*
* @param valueToUpdate Object to update
* @param updateWithValue Object to conceptually serialize and merge into value to
* update; can be thought of as a provider for overrides to apply.
*
* @return First argument, that is, `valueToUpdate`
*
* @throws JsonMappingException if there are structural incompatibilities that prevent update
*
* @since 2.9
*/
public <T> T updateValue(T valueToUpdate, Object updateWithValue)
throws JsonMappingException
{
if ((valueToUpdate == null) || (updateWithValue == null)) {
return valueToUpdate;
}
@SuppressWarnings("resource")
TokenBuffer buf = new TokenBuffer(this, false);
if (isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
buf = buf.forceUseOfBigDecimal(true);
}
try {
SerializationConfig config = getSerializationConfig().
without(SerializationFeature.WRAP_ROOT_VALUE);
_serializerProvider(config).serializeValue(buf, updateWithValue);
JsonParser p = buf.asParser();
readerForUpdating(valueToUpdate).readValue(p);
p.close();
} catch (IOException e) { // should not occur, no real i/o...
if (e instanceof JsonMappingException) {
throw (JsonMappingException) e;
}
// 17-Mar-2017, tatu: Really ought not happen...
throw JsonMappingException.fromUnexpectedIOE(e);
}
return valueToUpdate;
}

/*
/**********************************************************
/* Extended Public API: JSON Schema generation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1777,7 +1777,7 @@ protected JsonNode _detectBindAndCloseAsTree(InputStream in) throws IOException
* of given input
*/
protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match)
throws JsonProcessingException
throws JsonProcessingException
{
// 17-Aug-2015, tatu: Unfortunately, no parser/generator available so:
throw new JsonParseException(null, "Can not detect format from input, does not look like any of detectable formats "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class StringDeserializer extends StdScalarDeserializer<String> // non-fin
* @since 2.2
*/
public final static StringDeserializer instance = new StringDeserializer();

public StringDeserializer() { super(String.class); }

// since 2.6, slightly faster lookups for this very common type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,8 @@
* Unit tests for verifying that "updating reader" works as
* expected.
*/
public class TestUpdateValue extends BaseMapTest
public class TestUpdateViaObjectReader extends BaseMapTest
{
/*
/********************************************************
/* Helper types
/********************************************************
*/

static class Bean {
public String a = "a";
public String b = "b";
Expand All @@ -36,7 +30,6 @@ static class XYBean {
public int x, y;
}

// [JACKSON-824]
public class TextView {}
public class NumView {}

Expand Down Expand Up @@ -85,7 +78,7 @@ public DataA deserialize(JsonParser p, DeserializationContext ctxt) throws IOExc

/*
/********************************************************
/* Unit tests
/* Test methods
/********************************************************
*/

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.fasterxml.jackson.databind.convert;

import java.util.*;

import com.fasterxml.jackson.databind.*;

/**
* Tests for {@link ObjectMapper#updateValue}.
*
* @since 2.9
*/
public class UpdateValueTest extends BaseMapTest
{
/*
/********************************************************
/* Test methods; simple containers
/********************************************************
*/

private final ObjectMapper MAPPER = new ObjectMapper();

public void testMapUpdate() throws Exception
{
Map<String,Object> base = new LinkedHashMap<>();
base.put("a", 345);
Map<String,Object> overrides = new LinkedHashMap<>();
overrides.put("xyz", Boolean.TRUE);
overrides.put("foo", "bar");

Map<String,Object> ob = MAPPER.updateValue(base, overrides);
// first: should return first argument
assertSame(base, ob);
assertEquals(3, ob.size());
assertEquals(Integer.valueOf(345), ob.get("a"));
assertEquals("bar", ob.get("foo"));
assertEquals(Boolean.TRUE, ob.get("xyz"));
}

public void testListUpdate() throws Exception
{
List<Object> base = new ArrayList<>();
base.add(123456);
base.add(Boolean.FALSE);
Object[] overrides = new Object[] { Boolean.TRUE, "zoink!" };

List<Object> ob = MAPPER.updateValue(base, overrides);
// first: should return first argument
assertSame(base, ob);
assertEquals(4, ob.size());
assertEquals(Integer.valueOf(123456), ob.get(0));
assertEquals(Boolean.FALSE, ob.get(1));
assertEquals(overrides[0], ob.get(2));
assertEquals(overrides[1], ob.get(3));
}

// What are expected array semantics?
/*
public void testArrayUpdate() throws Exception
{
// Since Arrays are immutable, not sure what "right answer" ought to be
Object[] base = new Object[] { Boolean.FALSE, Integer.valueOf(3) };
Object[] overrides = new Object[] { Boolean.TRUE, "zoink!" };
Object[] ob = MAPPER.updateValue(base, overrides);
}
*/

/*
/********************************************************
/* Test methods; POJOs
/********************************************************
*/

public void testPOJO() throws Exception
{
Point base = new Point(42, 28);
Map<String,Object> overrides = new LinkedHashMap<>();
overrides.put("y", 1234);
Point result = MAPPER.updateValue(base, overrides);
assertSame(base, result);
assertEquals(42, result.x);
assertEquals(1234, result.y);
}

/*
/********************************************************
/* Test methods; other
/********************************************************
*/

public void testMisc() throws Exception
{
// if either is `null`, should return first arg
assertNull(MAPPER.updateValue(null, "foo"));
List<String> input = new ArrayList<>();
assertSame(input, MAPPER.updateValue(input, null));
}

}

0 comments on commit 8f8904a

Please sign in to comment.