Skip to content

Commit

Permalink
Special handling to support JsonIgnore for Records deserialization.
Browse files Browse the repository at this point in the history
  • Loading branch information
yihtserns committed Jan 14, 2023
1 parent bd89187 commit eaf013a
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,12 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
}
// regular property? needs buffering
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
// Special handling because Records' ignored creator props weren't removed (to help in creating
// constructor-backed PropertyCreator) so they ended up in _beanProperties, unlike POJO (whose ignored
// props are removed)
boolean isClassWithoutMutator = _beanType.isRecordType();

if (prop != null && !isClassWithoutMutator) {
try {
buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
} catch (UnresolvedForwardReference reference) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,17 @@ protected void _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props)
}
// Otherwise, check ignorals
if (prop.anyIgnorals()) {
// Special handling for Records, as they do not have mutators so relying on constructors with (mostly)
// implicitly-named parameters...
if (_classDef.getType().isRecordType()) {
// ...so can only remove ignored field and/or accessors, not constructor parameters that are needed
// for instantiation...
prop.removeIgnored();
// ...which will then be ignored (the incoming property value) during deserialization
_collectIgnorals(prop.getName());
continue;
}

// first: if one or more ignorals, and no explicit markers, remove the whole thing
// 16-May-2022, tatu: NOTE! As per [databind#3357] need to consider
// only explicit inclusion by accessors OTHER than ones with ignoral marker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
Expand All @@ -23,8 +22,6 @@ record SimpleRecord(int id, String name) { }

record RecordOfRecord(SimpleRecord record) { }

record RecordWithIgnore(int id, @JsonIgnore String name) { }

record RecordWithRename(int id, @JsonProperty("rename")String name) { }

record RecordWithHeaderInject(int id, @JacksonInject String name) { }
Expand Down Expand Up @@ -56,7 +53,6 @@ public void testClassUtil() {

assertTrue(ClassUtil.isRecordType(SimpleRecord.class));
assertTrue(ClassUtil.isRecordType(RecordOfRecord.class));
assertTrue(ClassUtil.isRecordType(RecordWithIgnore.class));
assertTrue(ClassUtil.isRecordType(RecordWithRename.class));
}

Expand All @@ -65,7 +61,6 @@ public void testRecordJavaType() {

assertTrue(MAPPER.constructType(SimpleRecord.class).isRecordType());
assertTrue(MAPPER.constructType(RecordOfRecord.class).isRecordType());
assertTrue(MAPPER.constructType(RecordWithIgnore.class).isRecordType());
assertTrue(MAPPER.constructType(RecordWithRename.class).isRecordType());
}

Expand Down Expand Up @@ -131,37 +126,10 @@ public void testDeserializeSimpleRecord_DisableAnnotationIntrospector() throws E

/*
/**********************************************************************
/* Test methods, ignorals, renames, injects
/* Test methods, renames, injects
/**********************************************************************
*/

public void testSerializeJsonIgnoreRecord() throws Exception {
String json = MAPPER.writeValueAsString(new RecordWithIgnore(123, "Bob"));
assertEquals("{\"id\":123}", json);
}

/**
* This test-case is just for documentation purpose:
* Because unlike JavaBean where the setter can be ignored, the Record's constructor argument must
* have value.
* <p/>
* You can make a constructor parameter optional by {@link JacksonInject}-ing a value, or by creating an alternative
* JsonCreator.mode=PROPERTIES constructor that excludes the ignored parameter.
*
* @see #testDeserializeConstructorInjectRecord()
* @see RecordCreatorsTest#testDeserializeWithAltCtor()
*/
public void testDeserializeJsonIgnoreRecord_WillFail() throws Exception {
try {
MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnore.class);

fail("should not pass");
} catch (InvalidDefinitionException e) {
verifyException(e, "Argument #1 of constructor");
verifyException(e, "must have name when multiple-parameter constructor annotated as Creator");
}
}

public void testSerializeJsonRename() throws Exception {
String json = MAPPER.writeValueAsString(new RecordWithRename(123, "Bob"));
final Object EXP = map("id", Integer.valueOf(123), "rename", "Bob");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.fasterxml.jackson.databind.records;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.ObjectMapper;

public class RecordWithJsonIgnoreTest extends BaseMapTest
{
record RecordWithIgnore(int id, @JsonIgnore String name) {
}

record RecordWithIgnoreJsonProperty(int id, @JsonIgnore @JsonProperty("name") String name) {
}

record RecordWithIgnoreAccessor(int id, String name) {

@JsonIgnore
@Override
public String name() {
return name;
}
}

record RecordWithIgnorePrimitiveType(@JsonIgnore int id, String name) {
}

private final ObjectMapper MAPPER = newJsonMapper();

/*
/**********************************************************************
/* Test methods, JsonIgnore
/**********************************************************************
*/

public void testSerializeJsonIgnoreRecord() throws Exception {
String json = MAPPER.writeValueAsString(new RecordWithIgnore(123, "Bob"));
assertEquals("{\"id\":123}", json);
}

public void testDeserializeJsonIgnoreRecord() throws Exception {
RecordWithIgnore value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnore.class);
assertEquals(new RecordWithIgnore(123, null), value);
}

/*
/**********************************************************************
/* Test methods, JsonIgnore + JsonProperty
/**********************************************************************
*/

public void testSerializeJsonIgnoreAndJsonPropertyRecord() throws Exception {
String json = MAPPER.writeValueAsString(new RecordWithIgnoreJsonProperty(123, "Bob"));
assertEquals("{\"id\":123}", json);
}

public void testDeserializeJsonIgnoreAndJsonPropertyRecord() throws Exception {
RecordWithIgnoreJsonProperty value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreJsonProperty.class);
assertEquals(new RecordWithIgnoreJsonProperty(123, "Bob"), value);
}

/*
/**********************************************************************
/* Test methods, JsonIgnore accessor
/**********************************************************************
*/

public void testSerializeJsonIgnoreAccessorRecord() throws Exception {
String json = MAPPER.writeValueAsString(new RecordWithIgnoreAccessor(123, "Bob"));
assertEquals("{\"id\":123}", json);
}

public void testDeserializeJsonIgnoreAccessorRecord() throws Exception {
RecordWithIgnoreAccessor value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnoreAccessor.class);
assertEquals(new RecordWithIgnoreAccessor(123, null), value);
}

/*
/**********************************************************************
/* Test methods, JsonIgnore parameter of primitive type
/**********************************************************************
*/

public void testSerializeJsonIgnorePrimitiveTypeRecord() throws Exception {
String json = MAPPER.writeValueAsString(new RecordWithIgnorePrimitiveType(123, "Bob"));
assertEquals("{\"name\":\"Bob\"}", json);
}

public void testDeserializeJsonIgnorePrimitiveTypeRecord() throws Exception {
RecordWithIgnorePrimitiveType value = MAPPER.readValue("{\"id\":123,\"name\":\"Bob\"}", RecordWithIgnorePrimitiveType.class);
assertEquals(new RecordWithIgnorePrimitiveType(0, "Bob"), value);
}
}

0 comments on commit eaf013a

Please sign in to comment.