I am using following mapper
ObjectMapper mapper = new ObjectMapper ();
mapper.configure(Feature.WRAP_ROOT_VALUE, true);
AnnotationIntrospector primary = new JaxbAnnotationIntrospector();
AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primary, secondary);
mapper.setAnnotationIntrospector(pair);
And serializing following class to JSON
public class MyList {
#JsonProperty("samples")
List<Module> sampleList;
public List<Module> getSampleList() {
return sampleList;
}
public void setSampleList(List<Sample> sampleList) {
this.sampleList = sampleList;
}
}
But I am getting the following output:
{"MyList ":{"samples":[ ... ]}}
But I do not want this MyList. I am expecting:
"samples":[...]
How can I tell Jackson to include the root only when it is specified explicitely?
Related
In a webservice developed in Spring Boot framework, I am looking for a way to filter few sensitive fields in the response. I am trying with JsonFilter. Here is the code I tried so far:
#ApiModel (value = "Customer")
#JsonInclude (JsonInclude.Include.NON_NULL)
#JsonFilter("CustomerFilter")
public class Customer implements Serializable
{
...
}
Controller code that sends filtered response:
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(customer);
FilterProvider filters = new
SimpleFilterProvider().setFailOnUnknownId(false).addFilter("CustomerFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("customerId"));
mappingJacksonValue.setFilters(filters);
return ResponseEntity.status(HttpStatus.OK).body(mappingJacksonValue);
While invoking the request, the following exception is thrown.
com.fasterxml.jackson.databind.JsonMappingException: Can not resolve PropertyFilter with id 'CustomerFilter'; no FilterProvider configured
Am I missing any configuration?
I was having the same issue this week, just resolved it now by creating a FilterConfiguration class in my config folder.
#JsonFilter("studentFilter")
public class Student {
String name;
String password;
public Student() {
this.name = "Steve";
this.password = "superSecretPassword";
}
}
#Configuration
public class FilterConfiguration {
public FilterConfiguration (ObjectMapper objectMapper) {
SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider().setFailOnUnknownId(true);
simpleFilterProvider.addFilter("studentFilter", SimpleBeanPropertyFilter.filterOutAllExcept("name"));
objectMapper.setFilterProvider(simpleFilterProvider);
}
}
When I create a new Student, the password is filtered out.
Avoid using MappingJacksonValue as it fails in object chaining and provide error like ["data"]->org.springframework.http.converter.json.MappingJacksonValue["value"]->java.util.ArrayList[0])
Note : Use ObjectMapper and ObjectWriter instead of MappingJacksonValue
Try the below code snippet
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
FilterProvider filters = new SimpleFilterProvider().setFailOnUnknownId(false).addFilter("CustomerFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("customerId"));
ObjectWriter writer = mapper.writer(filters);
String writeValueAsString = writer.writeValueAsString(customer);
Customer resultCustomer = mapper.readValue(writeValueAsString, Customer.class);
return ResponseEntity.status(HttpStatus.OK).body(resultCustomer);
I have a custom jackson serializer, and it works for serializing single pojos. Im trying to serialize a list of objects. Without the custom serializer I can just do:
public List<Sale> getAllSales() {
return saleRepository.getAll();
}
which works fine, but I want to return a very specific set of data, so I made a custom serializer, which also works but only for single objects:
public Sale getSale(int id) {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Sale.class, new SaleSerializer());
mapper.registerModule(module);
Sale sale = saleRepository.findById(1).orElse(null);
return mapper.writeValueAsString(sale);
}
How do I do the implement the custom serializer for a list of objects?
I'm not sure if this is the best way to do this, but I ended up doing it this way.
public ArrayNode getAllSalesToday() throws JsonProcessingException {
LocalDate localDate = LocalDate.now();
LocalDateTime startOfDay = localDate.atStartOfDay();
LocalDateTime endOfDay = localDate.atTime(LocalTime.MAX);
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Sale.class, new SaleSerializer());
mapper.registerModule(module);
List<Sale> saleList = saleRepository.getAllByInitialDepositDateIsBetween(startOfDay,endOfDay);
ArrayNode arrayNode = mapper.createArrayNode();
for (Sale sale: saleList){
String x = mapper.writeValueAsString(sale);
JsonNode jsonNode = mapper.readTree(x);
arrayNode.add(jsonNode);
}
return arrayNode;
}
I have gone through some of the posts but not find a suitable solution to my problem.
I am trying to serialize/deserialize a HashMap<String, Object>. Here the value of the hash map can anything?
when I add a class called "JobData", to Map for serialize/deserialize, I am seeing some issues when I deserialize the objectMapper.
Below is the sample code
public class JobData {
private FinalResult finalResult;
public FinalResult getFinalResult(){
return finalResult;
}
public void setFinalResult(FinalResult finalResult) {
this.finalResult = finalResult;
}
}
public class FinalResult<T> {
private Map<T, Exception> exceptionMap;
public HashMap<T, Exception> getFailedExceptionMap(){
return exceptionMap;
}
public void setFailedExceptionMap(Map<T, Exception> map){
exceptionMap = map;
}
}
Map<String, Object> data = new HashMap<String, Object>();
JobData jobdata = new JobData();
FinalResult result = new FinalResult();
Map<Integer, Exception> exceptionMap = new HashMap<Integer, Exception>();
exceptionMap.put(new Integer("1233456"), new Exception("MY_ERROR", "TESTING ERROR"));
result.setFailedExceptionMap(exceptionMap);
exportJobdata.setFinalResult(result);
data.put("JOB_DATA", jobdata);
...............
//Serialization
mapper.writerWithType(HashMap.class).writeValue(arg1, arg0);
.................
//Deserialize
HashMap<String, Object> map = mapper.readValue(in, new TypeReference<HashMap<String, Object>>()
Some of the links suggest to use TypeFactory for generic types? But not clear on how to use this?
Serialization works fine, But I am getting an error when try to deserialize
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token
(START_OBJECT), expected START_ARRAY: need JSON Array to contain
As.WRAPPER_ARRAY type information for class java.util.Map at [Source:
java.io.ByteArrayInputStream#13eb8acf; line: 1, column: 1251] (through
reference chain:
java.util.HashMap["JOB_DATA"]->com.sample.JobData["finalResult"]->com.sample.FinalResult["failedExceptionMap"])
at
com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:261)
cannot reproduce. This code worked flawlessly (jackson 2.8.0)
public static void main(String[] args)
{
Map<String, Object> data = new HashMap<String, Object>();
JobData jobdata = new JobData();
FinalResult<Integer> result = new FinalResult<>();
Map<Integer, Exception> exceptionMap = new HashMap<Integer, Exception>();
exceptionMap.put(new Integer("1233456"), new Exception("MY_ERROR"));
result.setFailedExceptionMap(exceptionMap);
jobdata.setFinalResult(result);
data.put("JOB_DATA", jobdata);
ObjectMapper mapper = new ObjectMapper();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
mapper.writerFor(HashMap.class).writeValue(os, data);
InputStream is = new ByteArrayInputStream(os.toByteArray());
HashMap<String, Object> map = mapper.readValue(is, new TypeReference<HashMap<String, Object>>(){});
System.out.println(map);
} catch (Exception e) {
e.printStackTrace();
}
}
output
{JOB_DATA={finalResult={failedExceptionMap={1233456={cause=null, stackTrace=[{methodName=main, fileName=JSONTest.java, lineNumber=36, className=test.JSONTest, nativeMethod=false}], localizedMessage=MY_ERROR, message=MY_ERROR, suppressed=[]}}}}}
Is there a way to serialize an object so that it could then be rehydrated by .Net Core Configuration Binder?
Basically, I'd like to get this Test to pass:
[Test]
public void Can_Serialize_And_Rehydrate()
{
var foo = new Foo{ Prop1 = 42; Prop2 = "Test" }
Dictionary<string, string> serialized = Serialize(Foo);
var deserializedFoo = new Foo();
new ConfigurationBuilder()
.AddInMemoryCollection(serialized)
.Build()
.Bind(deserializedFoo);
Assert.AreEqual(deserializedFoo.Prop1, 42);
Assert.AreEqual(deserializedFoo.Prop2, "Test");
}
Is there a Serializer out-of-the-box, or am I'm going to need to write my own Serialize() method?
AddInMemoryCollection's signature is like below, so why are you trying to serialize your dictionary here? You could just use it as it is.
public static IConfigurationBuilder AddInMemoryCollection(
this IConfigurationBuilder configurationBuilder,
IEnumerable<KeyValuePair<string, string>> initialData)
If you like to know more about how to test your custom configurations, I would suggest to look here:
https://github.com/aspnet/Configuration/blob/1.0.0/test/Microsoft.Extensions.Configuration.Binder.Test/ConfigurationBinderTests.cs
I was able to get this working by "hijacking" a JsonConfigurationProvider and plugging serialized Json directly into it. Not sure if this is the best way, but it does work:
public class ConfigurationSerializer
{
private class CustomJsonProvider : JsonConfigurationProvider
{
public CustomJsonProvider() : base(new JsonConfigurationSource())
{
}
public IDictionary<string, string> GetData(Stream s)
{
Load(s);
// Return the Configuration Dictionary
return Data;
}
}
public Dictionary<string, string> Serialize(object o)
{
var serialized =
JsonConvert.SerializeObject(
o,
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized)))
{
var jsonProvider = new CustomJsonProvider();
return jsonProvider
.GetData(ms)
.ToDictionary(key => key.Key, value => value.Value);
}
}
}
I can't seem to find out how to serialize Hibernate's implementation of constraint violations using Gson.
Here's what I've tried so far.
Approach 1
MyPojo aPojo = new MyPojo();
Gson gson = new Gson();
Set<ConstraintViolation<MyPojo>> violations = validator.validate(aPojo);
System.out.println(gson.toJson(violations));
Fails with this error:
java.lang.UnsupportedOperationException:
Attempted to serialize java.lang.Class: com.bar.baz.MyPojo.
Forgot to register a type adapter?
at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:67)
at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:107)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
at com.google.gson.Gson.toJson(Gson.java:593)
at com.google.gson.Gson.toJson(Gson.java:572)
at com.google.gson.Gson.toJson(Gson.java:527)
at com.google.gson.Gson.toJson(Gson.java:507)
Approach 2
Gson gson = new Gson();
Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);
System.out.println(
gson.toJson(violations,
new TypeToken<ConstraintViolation<MyPojo>>() {}.getType())
);
Fails by not serializing MyPojo's properties:
Output: {}.
Approach 3
I was expecting this approach to delegate serialization to my custom Serializer but it still fails:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
new TypeToken<ConstraintViolation<MyPojo>>() {}.getType(),
new JsonSerializer<ConstraintViolation<MyPojo>>() {
#Override
public JsonElement serialize(ConstraintViolation<MyPojo> src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.addProperty("aTestProperty", "A Test Value");
return result;
}
});
Gson gson = builder.create();
Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);
System.out.println(gson.toJson(violations));
However it fails with this error:
java.lang.UnsupportedOperationException:
Attempted to serialize java.lang.Class:
com.bar.baz.MyPojo.
Forgot to register a type adapter?
at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:67)
at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:107)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
at com.google.gson.Gson.toJson(Gson.java:593)
at com.google.gson.Gson.toJson(Gson.java:572)
at com.google.gson.Gson.toJson(Gson.java:527)
at com.google.gson.Gson.toJson(Gson.java:507)
Approach 4
Looking at the error message, I though this might work:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
new TypeToken<ConstraintViolation<MyPojo>>() {}.getType(),
new JsonSerializer<ConstraintViolation<MyPojo>>() {
#Override
public JsonElement serialize(ConstraintViolation<MyPojo> src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.addProperty("aTestProperty", "A Test Value");
return result;
}
});
builder.registerTypeAdapter(
new TypeToken<MyPojo>() {}.getType(),
new JsonSerializer<MyPojo>() {
#Override
public JsonElement serialize(MyPojo src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.addProperty("anotherTestProperty", "Another Test Value");
return result;
}
});
Gson gson = builder.create();
Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);
System.out.println(gson.toJson(violations));
But it fails with a similar error.
Approach 5: Working but ugly
The only thing that I've managed to make work is to register the serializer with the type of the vendor (Hibernate) specific implementation for ConstraintViolation:
Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
new TypeToken<ConstraintViolationImpl>() {}.getType(),
new JsonSerializer<ConstraintViolation<MyPojo>>() {
#Override
public JsonElement serialize(ConstraintViolation<MyPojo> src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.addProperty("aTestProperty", "A Test Value");
return result;
}
});
Gson gson = builder.create();
System.out.println(gson.toJson(violations));
Is there a way to make this work without relying on the concrete implementation of ConstraintViolation (i.e. org.hibernate.validator.internal.engine.ConstraintViolationImpl)?
There doesn't seem to be a reasonable approach to serialize javax.validation.ConstraintViolation objects. In fact, even Jackson errs while trying to serialize the set:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: fromIndex(0) > toIndex(-1) (through reference chain: java.util.HashSet[0]->org.hibernate.validator.internal.engine.ConstraintViolationImpl["propertyPath"]->org.hibernate.validator.internal.engine.path.PathImpl["pathWithoutLeafNode"]->org.hibernate.validator.internal.engine.path.PathImpl["pathWithoutLeafNode"]->org.hibernate.validator.internal.engine.path.PathImpl["pathWithoutLeafNode"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232)
For the time being, I just convert the set of errors into a set of custom POJOs I've written and serialize that instead.
Custom ValidationError POJO:
public class ValidationError {
private String className;
private String propertyPath;
private String errorMessage;
public static Set<ValidationError> fromViolations(Set violations) {
Set<ValidationError> errors = new HashSet<ValidationError>();
for (Object o : violations) {
ConstraintViolation v = (ConstraintViolation) o;
ValidationError error = new ValidationError();
error.setClassName(v.getRootBeanClass().getSimpleName());
error.setErrorMessage(v.getMessage());
error.setPropertyPath(v.getPropertyPath().toString());
errors.add(error);
}
return errors;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getPropertyPath() {
return propertyPath;
}
public void setPropertyPath(String propertyPath) {
this.propertyPath = propertyPath;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
#Override
public String toString() {
return "ValidationError{" +
"className='" + className + '\'' +
", propertyPath='" + propertyPath + '\'' +
", errorMessage='" + errorMessage + '\'' +
'}';
}
}
Sample usage:
Set<ConstraintViolation<MyBean>> violations = validator.validate(myBean);
Set<ValidationError> errors = ValidationError.fromViolations(violations);
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
System.out.println(gson.toJson(errors));
Update
For the sake of record, it is worth mentioning that XStream can serialize the set of constraint violations like a charm:
XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.setMode(XStream.NO_REFERENCES);
System.out.println(xstream.toXML(violations));
However the generated the object graph is way too much verbose and is not suitable for use in production anyway. You can see the sample output here.