I am trying to write Jackson deserializer module in Spring Boot app.
The main reason is to encrypt pin number from incoming request by using custom Jackson deserializer.
Encryption properties are provided by spring component CipherInterface
I was trying solution from this but my custom deserializer still was not called.
Instead of this based StringDeserializer is always called and no encryption is performed
Thanks in advance
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#JacksonAnnotation
public #interface Encrypted {
}
Request body with field to be encrypted
#Value
public class CardCounterDecreaseRequest {
#Encrypted
private final String pinValue;
}
Jackson configuration
#Bean
ObjectMapper unrestrictObjectMapper(final CipherInterface cipherInterface) {
return JsonMapper.builder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
.enable(SerializationFeature.INDENT_OUTPUT)
.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
.disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(DeserializationFeature.ACCEPT_FLOAT_AS_INT)
.visibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
.visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
.addModules(new EncryptionModule(cipherInterface), new JavaTimeModule(), new VavrModule(), new ParanamerModule())
.build();
}
Module:
public class EncryptionModule extends SimpleModule {
private final CipherInterface cipher;
public EncryptionModule(final CipherInterface cipher) {
super();
this.cipher = cipher;
}
#Override
public void setupModule(final SetupContext context) {
context.addBeanDeserializerModifier(new EncryptedDeserializerModifier(cipher));
}
}
public class EncryptedDeserializerModifier extends BeanDeserializerModifier {
private final CipherInterface cipher;
public EncryptedDeserializerModifier(final CipherInterface cipher) {
super();
this.cipher = cipher;
}
#Override
public BeanDeserializerBuilder updateBuilder(final DeserializationConfig config,
final BeanDescription beanDesc,
final BeanDeserializerBuilder builder) {
final Iterator<SettableBeanProperty> it = builder.getProperties();
while (it.hasNext()) {
final SettableBeanProperty prop = it.next();
if (null != prop.getAnnotation(Encrypted.class)) {
final JsonDeserializer<Object> current = prop.getValueDeserializer(); // always returns null
final EncryptedJsonDeserializer encryptedDeserializer = new EncryptedJsonDeserializer(cipher, current);
final SettableBeanProperty propWithEncryption = prop.withValueDeserializer(encryptedDeserializer);
builder.addOrReplaceProperty(propWithEncryption, true);
}
}
return builder;
}
}
And finally deserializer:
public class EncryptedJsonDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
private final CipherInterface service;
private final JsonDeserializer<Object> baseDeserializer;
private final BeanProperty property;
public EncryptedJsonDeserializer(final CipherInterface service, final JsonDeserializer<Object> baseDeserializer) {
this.service = service;
this.baseDeserializer = baseDeserializer;
this.property = null;
}
public EncryptedJsonDeserializer(final CipherInterface service, final JsonDeserializer<Object> wrapped, final BeanProperty property) {
this.service = service;
this.baseDeserializer = wrapped;
this.property = property;
}
#Override
public Object deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonDeserializer<?> deserializer = baseDeserializer;
if (deserializer instanceof ContextualDeserializer) {
deserializer = ((ContextualDeserializer) deserializer).createContextual(ctxt, property);
}
return // encryption logic here
}
#Override
public JsonDeserializer<?> createContextual(final DeserializationContext ctxt, final BeanProperty property) throws JsonMappingException {
JsonDeserializer<Object> wrapped = ctxt.findRootValueDeserializer(property.getType());
return new EncryptedJsonDeserializer(service, wrapped, property);
}
Just try below code, as you had created deserializer correctly but you are not informing spring that while deserialize this entity use below Custom desierializer class.
Add this extra line #JsonDeserialize(using = EncryptedJsonDeserializer.class) and try once.
#Value
#JsonDeserialize(using = EncryptedJsonDeserializer.class)
public class CardCounterDecreaseRequest {
#Encrypted
private final String pinValue;
}
It will help you.
Related
I'm writing some junit tests for my rest webservice. When I run my test, I got my response from the webservice. So I use objectMapper to map the json to my POJO class. However it fails on this line :
return objectMapper.readValue(json, clazz);
Error : com.fasterxml.jackson.databind.JsonMappingException: zoneId (through reference chain: com.customer.CustomerResponse["customerDetails"]->com.customer.CustomerDetails["licenseDate"])
This is because I'm using this annotation in my POJO class : #JsonDeserialize(using = CustomDateDeserializer.class) and in the Deserializer, I have a variable timezone , where the value is taken from properties file. It seems that the object Mapper is not able to resolve this properties in my Deserializer class :
#Value("${current.timezone}")
private String timezone;
Can you please help how to resolve this issue ? Thanks
#Transactional
#Rollback
class CustomerServiceIntegrationTest {
#Autowired
private DbUnitUtils dbUnitUtils;
protected MockMvc mvc;
#BeforeEach
void setup() throws Exception {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#BeforeEach
void setupData() throws Exception {
dbUnitUtils.initialiseDataSet(new File("src/test/resources/dbunit/test-dataset.xml"),
DatabaseOperation.CLEAN_INSERT);
}
#Test
void listCustomer_WhenIdExists_Then_ReturnCustomerInfo() throws Exception {
/*
* setup test-data
*/
Long idCustomer= 15L;
String uri = "/customer/" + 15L;
/*
* invoke endpoint being tested
*/
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri).accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
/*
* Verification
*/
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
CustomerResponse customerResponse =
mapFromJson(mvcResult.getResponse().getContentAsString(), CustomerResponse.class);
assertEquals(idCustomer, customerResponse.getId());
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module =
new SimpleModule("CustomDateDeserializer", new Version(1, 0, 0, null, null, null));
module.addDeserializer(ZonedDateTime.class, new CustomDateDeserializer());
objectMapper.registerModule(module);
return objectMapper.readValue(json, clazz);
}
}
public class CustomerResponse{
String name;
String address;
String phoneNo;
CustomerDetails customerDetails;
//getter and setter
}
public class CustomerDetails {
#JsonDeserialize(using = CustomDateDeserializer.class)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = Utils.FORMAT_DATE)
private ZonedDateTime licenseDate;
//getter and setters
}
public class CustomDateDeserializer extends StdDeserializer<ZonedDateTime> {
#Serial
private static final long serialVersionUID = 1L;
#Value("${current.timezone}")
private String timezone;
public CustomDateDeserializer () {
super((Class<?>) null);
}
protected CustomDateDeserializer (Class<?> vc) {
super(vc);
}
#Override
public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws
IOException {
String stringValue = p.getCodec().readValue(p, String.class);
if (StringUtils.isBlank(stringValue)) {
return null;
}
LocalDate localDate = LocalDate.parse(stringValue);
return localDate.atStartOfDay().atZone(ZoneId.of(timezone));
}
}
}
I want to convert a json into Java class by having custom deserializer.
I'm able to serialize ACC_NUM, NAME and any other fields from json but not sure what can be done to convert MOBILE_NUMBER_1,MOBILE_NUMBER_2 like fields into single JSONArray(See AccountInfo class). There can be many more fields like this and count also is not fixed. Example there can be ADDRESS_1, ADDRESS_2 till ADDRESS_20 and so on and all this fields should go in JSONArray of ADDRESS after deserilization.
I have a Map of Map which holds info like this:
{
"accountInfo": {
"ACC_NUM": "1234567890",
"NAME": "John Cena",
"MOBILE_NUMBER_1": "12376534",
"MOBILE_NUMBER_2": "12376534",
"MOBILE_NUMBER_3": "12376534",
"MOBILE_NUMBER_4": "12376534"
},
"someOther": {
//similer to above
}
}
This info I want to convert to the following class CommonInfo:
public class CommonInfo {
private AccountInfo accountInfo;
//other properties...
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class AccountInfo {
#JsonProperty("ACC_NUM")
private FieldValue<BigInteger> accountNum;
#JsonProperty("NAME")
private FieldValue<String> name;
#JsonProperty("MOBILE_NUMBER")
private FieldValue<JSONArray> mobileNumber;
}
//FieldValue class
public interface FieldValue<T> {
T getInitialValue();
void setInitialValue(T initialValue);
T getValue();
void setValue(T value);
}
#JsonInclude(JsonInclude.Include.ALWAYS)
public class FieldValueImpl<T> implements FieldValue<T> {
protected T initialValue;
protected T value;
//getters, setters, cons..
}
My service code takes json/Map and tries to convert it to CommonInfo class
#Service
public class MyService {
private final ObjectMapper jsonMapper = new ObjectMapper();
#PostConstruct
protected void init() {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(FieldValue.class, new FieldValueSerializer());
simpleModule.addDeserializer(FieldValue.class, new FieldValueDeserializer());
jsonMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
jsonMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
jsonMapper.registerModule(simpleModule);
}
public CommonInfo setPojoResult(Map<String, LinkedHashMap<String, String>> contentAsMap) {
return jsonMapper.convertValue(contentAsMap, CommonInfo.class);
}
}
Serializer and Deserializer looks like this:
public class FieldValueDeserializer extends JsonDeserializer<FieldValue<?>> implements ContextualDeserializer {
private JavaType valueType;
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
var deserializer = new FieldValueDeserializer();
if (property == null) {
deserializer.valueType = ctxt.getContextualType().containedType(0);
} else {
var wrapperType = property.getType();
var valueType = wrapperType.containedType(0);
deserializer.valueType = valueType;
}
return deserializer;
}
#Override
public FieldValue<?> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
FieldValueDeserializer deserializer = new FieldValueDeserializer();
deserializer.getKnownPropertyNames();
FieldValue<?> fieldValueImpl = new FieldValueImpl<>();
if (valueType.toString().contains("java.time.LocalDate")) {
JsonNode node = parser.getCodec().readTree(parser);
FieldValue<LocalDate> f1 = new FieldValueImpl<>();
f1.setValue(DateUtils.convertJulianToLocalDate(node.textValue()));
return f1;
} else {
fieldValueImpl.setValue(context.readValue(parser, valueType));
}
return fieldValueImpl;
}
}
//--
public class FieldValueSerializer extends StdSerializer<FieldValue> {
public FieldValueSerializer() {
this(null);
}
public FieldValueSerializer(Class<FieldValue> vc) {
super(vc);
}
#Override
public void serialize(FieldValue value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString(String.valueOf(value.getCurValue()));
}
}
I want to map some fields of json to inner fields of a class. e.g
{
values:[{
"name":"Abc",
"age":18,
"street":"test",
"postalcoad":"1231412"
},
{
"name":"ccvb",
"age":20,
"street":"test2",
"postalcoad":"123"
}
]}
Following i my java class
#JsonIgnoreProperties(ignoreUnknown = true)
public class Customer{
#JsonProperty("name")
private string name;
#JsonProperty("age")
private int age;
private Address address;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class Address{
#JsonProperty("street")
private string street;
#JsonProperty("postalcode")
private string postalcode;
}
ObjectMapper mapper = new ObjectMapper();
Customer[] c = mapper.readValue(mapper.readTree(json).get("values").toString(), Customer[].class);
It returns me Customer object without Address. Any idea how can i create Address object from this json.
One of the options is to use #JsonCreator annotation:
#JsonCreator
public Customer(
#JsonProperty("name") String name,
#JsonProperty("age") int age,
#JsonProperty("street") String street,
#JsonProperty("postalcode") String postalcode
) {
this.name = name;
this.age = age;
this.address = new Address();
this.address.street = street;
this.address.postalcode = postalcode;
}
Second option is create custom deserializer and bind your class with deserializer using #JsonDeserialize annotation
#JsonDeserialize(using = CustomerDeserializer.class)
public static class Customer{
....
}
public class CustomerDeserializer extends StdDeserializer<Customer> {
public CustomerDeserializer() {
super(Customer.class);
}
#Override
public Customer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Customer customer = new Customer();
JsonNode treeNode = p.readValueAsTree();
if (treeNode == null) {
return null;
}
customer.setName(treeNode.get("name").asText());
customer.setAge(treeNode.get("age").asInt());
Address address = new Address();
address.setStreet(treeNode.get("street").asText());
address.setPostalcode(treeNode.get("postalcode").asText());
customer.setAddress(address);
return customer;
}
}
As third option, you can use #JsonAnySetter with some kind of post construct processing:
public interface PostConstruct {
void postConstruct();
}
public class Customer implements PostConstruct {
//mapping
private Map<String, Object> additionalFields = new HashMap<>();
#JsonAnySetter
public void setAdditionalValue(String key, Object value) {
additionalFields.put(key, value);
}
#Override
public void postConstruct() {
address = new Address();
address.setStreet(String.valueOf(additionalFields.get("street")));
address.setPostalcode(String.valueOf(additionalFields.get("postalcode")));
}
}
public static class PostConstructDeserializer extends DelegatingDeserializer {
private final JsonDeserializer<?> deserializer;
public PostConstructDeserializer(JsonDeserializer<?> deserializer) {
super(deserializer);
this.deserializer = deserializer;
}
#Override
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
return deserializer;
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Object result = _delegatee.deserialize(jp, ctxt);
if (result instanceof PostConstruct) {
((PostConstruct) result).postConstruct();
}
return result;
}
}
//using of post construct deserializer
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc,
final JsonDeserializer<?> deserializer) {
return new PostConstructDeserializer(deserializer);
}
});
mapper.registerModule(module);
I would create a custom deserializer and inside of it call the default deserializer for Customer and then call the default deseriazlier for Address. Then you add the address to the customer object. This way they both look at the same json but you get two different objects out and you can connect them the way you want.
To call a standard deserializer from a custom deseriazlier see this answer: How do I call the default deserializer from a custom deserializer in Jackson.
In my pojo class I have configured a CustomDeserializer with annotation
#JsonDeserialize(using = CustomDeserializer.class)
class Myclass {
private String A;
#JsonIgnore
private String B;
#JsonIgnore
private String C;
private String D;
...
private String Z;
/*getters and setters*/
}
In CustomDeserializer, I want to manage only some of the fields and leave the rest for Jackson to manage.
CustomDeserializer.java
public class CustomDeserializer extends StdDeserializer<Myclass > {
private static final long serialVersionUID = 4781685606089836048L;
public CustomDeserializer() {
super(Myclass.class);
}
#Override
public Myclass deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, IllegalResponseException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
ObjectNode root = (ObjectNode) mapper.readTree(jp);
Myclass myClass = mapper.readValue(root.toString(), Myclass.class);
//--- HERE MANAGE FIELD B ---
myClass.setB(myNewB);
//--- HERE MANAGE FIELD C ---
myClass.setC(myNewC);
return myClass;
}
}
This way I run into an infinite loop because of the following line:
mapper.readValue(root.toString(), Myclass.class);
Is there a way to set default behavior when using Jackson so that I can exclude my CustomDeserializer?
The problem is that you will need a fully constructed default deserializer; and this requires that one gets built, and then your deserializer gets access to it. DeserializationContext is not something you should either create or change; it will be provided by ObjectMapper.
To meet your requirement you can start by writing a BeanDeserializerModifier and registering it via SimpleModule.
The following example should work:
public class CustomDeserializer extends StdDeserializer<Myclass> implements ResolvableDeserializer
{
private static final long serialVersionUID = 7923585097068641765L;
private final JsonDeserializer<?> defaultDeserializer;
public CustomDeserializer (JsonDeserializer<?> defaultDeserializer)
{
super(Myclass.class);
this.defaultDeserializer = defaultDeserializer;
}
#Override public Myclass deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
Myclass deserializedMyclass = (Myclass) defaultDeserializer.deserialize(jp, ctxt);
// custom logic
return deserializedMyclass;
}
// You have to implement ResolvableDeserializer when modifying BeanDeserializer
// otherwise deserializing throws JsonMappingException
#Override public void resolve(DeserializationContext ctxt) throws JsonMappingException
{
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException
{
SimpleModule module = new SimpleModule();
//Writing a new BeanDeserializerModifier
module.setDeserializerModifier(new BeanDeserializerModifier()
{
#Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
{
if (beanDesc.getBeanClass() == Myclass.class)
return new CustomDeserializer(deserializer);
return deserializer;
}
});
//register the BeanDeserializerModifier via SimpleModule
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Myclass myclass = mapper.readValue(new File("d:\\test.json"), Myclass.class);
}
}
I want to use custom JSON deserializer for some classes(Role here) but I can't get it working. The custom deserializer just isn't called.
I use Spring Boot 1.2.
Deserializer:
public class ModelDeserializer extends JsonDeserializer<Role> {
#Override
public Role deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return null; // this is what should be called but it isn't
}
}
Controller:
#RestController
public class RoleController {
#RequestMapping(value = "/role", method = RequestMethod.POST)
public Object createRole(Role role) {
// ... this is called
}
}
#JsonDeserialize on Role
#JsonDeserialize(using = ModelDeserializer.class)
public class Role extends Model {
}
Jackson2ObjectMapperBuilder bean in Java Config
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.deserializerByType(Role.class, new ModelDeserializer());
return builder;
}
What am I doing wrong?
EDIT It is probably caused by #RestController because it works with #Controller...
First of all you don't need to override Jackson2ObjectMapperBuilder to add custom deserializer. This approach should be used when you can't add #JsonDeserialize annotation. You should use #JsonDeserialize or override Jackson2ObjectMapperBuilder.
What is missed is the #RequestBody annotation:
#RestController
public class JacksonCustomDesRestEndpoint {
#RequestMapping(value = "/role", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Object createRole(#RequestBody Role role) {
return role;
}
}
#JsonDeserialize(using = RoleDeserializer.class)
public class Role {
// ......
}
public class RoleDeserializer extends JsonDeserializer<Role> {
#Override
public Role deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// .................
return something;
}
}
There is also another pretty interesting solution which can be helpful in case when you want to modify your JSON body before calling default deserializer. And let's imagine that you need to use some additional bean for that (use #Autowire mechanism)
Let's image situation, that you have the following controller:
#RequestMapping(value = "/order/product", method = POST)
public <T extends OrderProductInterface> RestGenericResponse orderProduct(#RequestBody #Valid T data) {
orderService.orderProduct(data);
return generateResponse();
}
Where OrderProductInterface is:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonSerialize(include = NON_EMPTY)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, visible = true, property = "providerType")
#JsonSubTypes({
#JsonSubTypes.Type(value = OrderProductForARequestData.class, name = "A")
})
public interface OrderProductInterface{}
The code above will provide dynamic deserialization base on filed providerType and validation according to concrete implementation. For better grasp, consider that OrderProductForARequestData can be something like that:
public class OrderProductForARequestData implements OrderProductInterface {
#NotBlank(message = "is mandatory field.")
#Getter #Setter
private String providerId;
#NotBlank(message = "is mandatory field.")
#Getter #Setter
private String providerType;
#NotBlank(message = "is mandatory field.")
#Getter #Setter
private String productToOrder;
}
And let's image now that we want to init somehow providerType (enrich input) before default deserialization will be executed. so the object will be deserialized properly according to the rule in OrderProductInterface.
To do that you can just modify your #Configuration class in the following way:
//here can be any annotation which will enable MVC/Boot
#Configuration
public class YourConfiguration{
#Autowired
private ObjectMapper mapper;
#Autowired
private ProviderService providerService;
#Override
public void setup() {
super.setup();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (beanDesc.getBeanClass() == OrderProductInterface.class) {
return new OrderProductInterfaceDeserializer(providerService, beanDesc);
}
return deserializer;
}
});
mapper.registerModule(module);
}
public static class OrderProductInterfaceDeserializer extends AbstractDeserializer {
private static final long serialVersionUID = 7923585097068641765L;
private final ProviderService providerService;
OrderProductInterfaceDeserializer(roviderService providerService, BeanDescription beanDescription) {
super(beanDescription);
this.providerService = providerService;
}
#Override
public Object deserializeWithType(JsonParser p, DeserializationContext context, TypeDeserializer typeDeserializer) throws IOException {
ObjectCodec oc = p.getCodec();
JsonNode node = oc.readTree(p);
//Let's image that we have some identifier for provider type and we want to detect it
JsonNode tmp = node.get("providerId");
Assert.notNull(tmp, "'providerId' is mandatory field");
String providerId = tmp.textValue();
Assert.hasText(providerId, "'providerId' can't be empty");
// Modify node
((ObjectNode) node).put("providerType",providerService.getProvider(providerId));
JsonFactory jsonFactory = new JsonFactory();
JsonParser newParser = jsonFactory.createParser(node.toString());
newParser.nextToken();
return super.deserializeWithType(newParser, context, typeDeserializer);
}
}
}