问题
In my REST microservice, I request an object from another service that returns me a huge nested JSON, for example:
{
"address": "19th Street",
"contact_info": {
"phones": {
"home": {
"first": {
"another_nested": 1234
}
}
}
}
}
What I need to fetch this data from another service, perform a change only in the first field, and then send it via HTTP. What I'm trying to avoid is to deserialize everything on my side and having to maintain the classes here.
Is there a way to get the raw value of contact_info
and just have the representation of address with Jackson? Something like this:
public class FullAddress {
String address;
RawValue contactInfo;
}
回答1:
The simplest approach would be using JsonNode
:
@Data
public class FullAddress {
private String address;
private JsonNode contactInfo;
}
Or either Map<String, Object>
:
@Data
public class FullAddress {
private String address;
private Map<String, Object> contactInfo;
}
It works for both serialization and deserialization.
If you, however, wants to store the raw JSON, then you could define a custom deserializer:
public class RawJsonDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
return mapper.writeValueAsString(node);
}
}
And then use use it as follows:
@Data
public class FullAddress {
private String address;
@JsonDeserialize(using = RawJsonDeserializer.class)
private String contactInfo;
}
For serializing back, however, you can annotate the contactInfo
field with @JsonRawValue.
回答2:
You can use the JsonIgnoreProperties
on class docs so it will ignore all other properties that not present in class
// To ignore any unknown properties in JSON input without exception:
@JsonIgnoreProperties(ignoreUnknown=true)
回答3:
There is also ObjectMapper.readerForUpdating
which basically allows you to update in place a JSON object. There's not much documentation for it though:
https://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/ObjectMapper.html#readerForUpdating-java.lang.Object-
Example here: https://www.logicbig.com/tutorials/misc/jackson/reader-for-updating.html
(Apologies for only a partial answer here - but it might be of use for someone)
回答4:
I recommend to always define model classes for your JSON string even if you need to map only a few fields from that JSON string. It's neater and in future anybody can easily add additional fields to model if it will be needed. Jackson supports partial mapping.
Complete working example in Kotlin:
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Test
class Test {
data class ModelInner(
var attributeB1: String? = null,
)
data class ModelRoot(
var attributeA2: List<ModelInner>? = null,
)
@Test
fun test0() {
val json = """
{
"attributeA0": "A0",
"attributeA1": "A1",
"attributeA2": [
{
"attributeB0": "B00",
"attributeB1": "B01"
},
{
"attributeB0": "B10",
"attributeB1": "B11"
}
]
}
"""
val om = ObjectMapper()
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val order = om.readValue(json, ModelRoot::class.java)
print(order)
}
}
Output:
ModelRoot(attributeA2=[ModelInner(attributeB1=B01), ModelInner(attributeB1=B11)])
回答5:
Another solution to this is to use @JsonAnyGetter
@JsonAnySetter
. See the write-up here:
https://www.concretepage.com/jackson-api/jackson-jsonanygetter-and-jsonanysetter-example
来源:https://stackoverflow.com/questions/58102069/how-to-do-a-partial-deserialization-with-jackson