How to deserialize Spring Boot actuator environment - java

I would like to deserialize the Spring Boot Environment object returned by:
http://hostname:port/actuator/env
I'm using Jackson's ObjectMapper:
private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
...
ClientResponse clientResponse = resource.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
InputStream is = clientResponse.getEntityInputStream();
org.springframework.core.env.Environment e = mapper.readValue(is, org.springframework.core.env.Environment.class);
The code above fails with the following error, which makes sense:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.springframework.core.env.Environment, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
But I've tried all the implementations of the Environment class (AbstractEnvironment, MockEnvironment, StandardEnvironment, StandardServletEnvironment) and they all fail as well.
Which class should I use?

org.springframework.core.env.Environment is an interface. So ObjectMapper can not guess what concrete class to instantiate. You need to tell your ObjectMapper which class to use. So in your line
org.springframework.core.env.Environment e = mapper.readValue(is,org.springframework.core.env.Environment.class); You need to replace org.springframework.core.env.Environment.class with some concrete class. For example org.springframework.core.env.StandardEnvironment (depending on what kind of environment actually being returned). Otherwise de-serialize it to map:
Map<String, Object> map = mapper.readValue(is,HashMap<String, Object>.class);
And then go from there

Related

How can I read yaml (Configuration) file as JSON in dropwizard?

I want to parse configuration yaml file as a JSON Object in Dropwizard.
As in Python and Ruby we can directly read yaml file as JSON:
require 'yaml'
conf = YAML.load_file('some.yml')
puts conf.<some key>
I want to follow the same protocol and do not want to make a separate class to map the yaml structure, but not sure how to achieve it in Java.
If I understand correctly, instead of defining a class to represent the structure of you JSON or YAML, you want to retrieve an object that gives you access to the JSON object in a generic manner.
Even though this is a bit contrary to the way dropwizard wants to do things (i.e., having typed class to access configuration data), it is relatively easy:
You need an instance of Jackson ObjectMapper: the one provided by dropwizard or you own, YAML-aware (see example below).
You can call the readTree method to transform an input into a JsonNode which gives you access to the JSON/YAML object in memory.
See ObjectMapper#readTree and similar methods.
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
JsonNode json = mapper.readTree(new File("some.yml"));
System.out.println(json.get("test"));
You can just use the Configuration object from DropWizard, either the default or a custom one that has been created to hold additional parameters. This object is available on the Application class, under the "run" method in particular. Using the DW configuration example:
public class ExampleConfiguration extends Configuration {
#Valid
#NotNull
private MessageQueueFactory messageQueue = new MessageQueueFactory();
#JsonProperty("messageQueue")
public MessageQueueFactory getMessageQueueFactory() {
return messageQueue;
}
#JsonProperty("messageQueue")
public void setMessageQueueFactory(MessageQueueFactory factory) {
this.messageQueue = factory;
}
}
You can get/set all the attributes from YAML file as below:
public class MyApplication extends Application<ExampleConfiguration> {
...
public void run(ExampleConfiguration configuration, Environment environment) {
configuration.getMessageQueueFactory();
configuration.getServerFactory();
}
}
Jackson can both parse yaml and format to JSON.
See an example here.

Where to register custom ObjectMapper for my REST Service application?

I am building a REST service application with Spring and Jersey.
The "Response Content Type" can be either JSON or XML, which is why I use JAXB annotations on the fields of my POJO, which works great so far. No configuration was needed to get this to work, I just put the required .jars on the classpath.
Now, I want to add custom behaviour for (de-)serialization of Java 8 Date/Time objects and register the JavaTimeModule from Jackson.
Where would be the right place to register the Module? I know the code snippet is supposed to look something like this:
ObjectMapper tmpMapper = new ObjectMapper();
tmpMapper.registerModule(new JavaTimeModule());
But where do I actually put this code?
The place that comes to mind is my ResourceConfig class from Jersey. It looks like this:
#ApplicationPath("/rest")
public class ApplicationResourceConfig extends ResourceConfig {
public ApplicationResourceConfig() {
// Not needed because since jersey 2.9 JacksonFeature implements AutoDiscoverable
// register(JacksonFeature.class);
register(WadlFeature.class);
// Registers our own request listener for monitoring purposes
register(RestServiceApplicationEventListener.class);
// Registers the package that contains our REST resources
packages(PingResource.class.getPackage().getName());
}
What I did now was to add the following lines at the bottom of the constructor:
ObjectMapper tmpMapper = new ObjectMapper();
tmpMapper.registerModule(new JavaTimeModule());
JacksonJaxbJsonProvider tmpProvider = new JacksonJaxbJsonProvider();
tmpProvider.setMapper(tmpMapper);
register(tmpProvider);
The DateTime conversion now worked. However, some of the the JAXB behaviour changed which caused the deserialization of other beans to break. Here is what is not working anymore:
I have super class which defines a getter of a field:
public abstract class SuperClass {
#XmlElement(name = "yourField")
public abstract String getMyField();
}
(This maps the internal name of the field to different name which is exposed to the outside)
public class SubClass {
private String myField;
#Override
public String getMyField() {
return myField;
}
}
This mechanism used to work before and I could e.g. define a POST parameter of type SubClass calling the method with a JSON snippet that looks like this:
{
"yourField" : "hello world"
}
When trying this now I get the exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "yourField" (class com.myProject.SubClass), not marked as ignorable (1 known property: "myField"])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#728b1889; line: 4, column: 14] (through reference chain: com.myProject.Subclass["yourField"])
Note however, that the basic JAXB features still work, only in this particular superclass/subclass example.
Could it be that explicitly registering a JacksonJaxbJsonProvider somehow altered the default behaviour on how to deal with JAXB annotated beans? Is there a way to maybe retrieve the existing JacksonJaxbJsonProvider (that must have been registered implicitly somehow) or the existing ObjectMapper object and register the JavaTimeModule to it instead of creating a new Mapper object and a new Provider object?

Jackson changes order of private variables when changing to json? [duplicate]

I'm implementing a RESTful web service where user has to send a signed verification token along with the request so that I could ensure that the request has not been tampered by a middle man. My current implementation is as follows.
Verification token is a VerifData object serialized into a String and then hashed and encrypted.
class VerifData {
int prop1;
int prop2;
}
In my service, I put data to be serialized into an instance of VerifData and then serialize it using Jackson ObjectMapper and passed along to the verification engine along with the verification token.
VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);
But it seems that each time the application container is started, the order of properties being mapped into a string by ObjectMapper changes.
Ex: one time it would be
{"prop1":12345,"prop2":67890}
and another time it would be
{"prop2":67890,"prop1":12345}
So if client has serialized the VerifData instance as into the first String, there is 50% chance of it being failed even though it is correct.
Is there a way to get around this? Can I specify the order of properties to map by ObjectMapper (like in ascending order)? Or is there any other way to best implement this verification step. Both client and server implementations are developed by me. I use Java Security API for signing and verifying.
The annotations are useful, but can be a pain to apply everywhere. You can configure your whole ObjectMapper to work this way with
Current Jackson versions:
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
Older Jackson versions:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
From the Jackson Annotations documentation:
// ensure that "id" and "name" are output before other properties
#JsonPropertyOrder({ "id", "name" })
// order any properties that don't have explicit setting using alphabetic order
#JsonPropertyOrder(alphabetic=true)
The following 2 ObjectMapper configuration are required:
ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
or
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
defines the property serialization order used for POJO fields
Note: does not apply to java.util.Map serialization!
and
ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
or
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
Feature that determines whether java.util.Map entries are first sorted by key before serialization
Spring Boot config example (yaml):
spring:
jackson:
mapper:
SORT_PROPERTIES_ALPHABETICALLY: true
serialization:
ORDER_MAP_ENTRIES_BY_KEYS: true
In Spring Boot you can add this behaviour globally by adding the following to your Application entry point class:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
In Jackson 2.x, which you are probably using today, use:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
If you care about looks, you may also consider SerializationFeature.INDENT_OUTPUT as well.
Note that you must serialize Maps or Objects for this to sort correctly. If you serialize a JsonNode for example (from readTree), that won't be properly indented.
Example
import com.fasterxml.jackson.databind.*;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
String input = "{\"hello\": {\"cruel\" : \"world\"} }";
Object pojo = mapper.readValue(input, Object.class);
System.out.println(mapper.writeValueAsString(pojo));
results in:
{
"hello" : {
"cruel" : "world"
}
}
There is an easier way in Spring Boot by specifying a property (in application.properties for example:
spring.jackson.mapper.sort_properties_alphabetically=true
From Duncan McGregor's answer:
Its better to use it like this:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
as MapperFeature is for XMLs and comes with jackson-databind which is not required...
I discovered yet another way today in case alphabetic is not your desired sorting order. It turns out adding a #JsonProperty annotation on a field places it last when writing if the rest of the fields are not annotated. I discovered that when I wanted to specify a property name which did not conform to java naming conventions.
By Adding an index attribute you can define the order. Lowest index is placed first.
#JsonProperty(index=20)
String prop1;
#JsonProperty(index=10)
String prop2;
Would render:
{"prop2": "valueProp2", "prop1": "valueProp1"}
You can use mix-in and specify the order of properties as you like:
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
public final class ObjectMapperUtils {
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.addMixIn(Object.class, IdFirst.class);
}
#Bean
public ObjectMapper objectMapper() {
return MAPPER;
}
#JsonPropertyOrder({"id", "...", "..."})
private abstract static class IdFirst {}
}
I realize this is an old thread, but since I was looking or an answer and landed here, some additional info could be handy for other people.
The #JsonProperty annotation I am using currently (jackson-annotations-2.11.2) accepts, besides the "value" argument, an "index" (numeric) argument that specifies the order of the fields during serialization.
Instead of using flag argument:
objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
As #Gary Rowe mentioned, we can use Jackson2ObjectMapperBuilder to sort the properties globally.
However for this to work, you must have Jackson2ObjectMapperBuilder in your classpath. It is not part of the Jackson library.
As per this documentation, spring-web dependency has Jackson2ObjectMapperBuilder file and should be in your classpath.
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
You can refer to this for other possible solutions

Jackson ObjectMapper - specify serialization order of object properties

I'm implementing a RESTful web service where user has to send a signed verification token along with the request so that I could ensure that the request has not been tampered by a middle man. My current implementation is as follows.
Verification token is a VerifData object serialized into a String and then hashed and encrypted.
class VerifData {
int prop1;
int prop2;
}
In my service, I put data to be serialized into an instance of VerifData and then serialize it using Jackson ObjectMapper and passed along to the verification engine along with the verification token.
VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);
But it seems that each time the application container is started, the order of properties being mapped into a string by ObjectMapper changes.
Ex: one time it would be
{"prop1":12345,"prop2":67890}
and another time it would be
{"prop2":67890,"prop1":12345}
So if client has serialized the VerifData instance as into the first String, there is 50% chance of it being failed even though it is correct.
Is there a way to get around this? Can I specify the order of properties to map by ObjectMapper (like in ascending order)? Or is there any other way to best implement this verification step. Both client and server implementations are developed by me. I use Java Security API for signing and verifying.
The annotations are useful, but can be a pain to apply everywhere. You can configure your whole ObjectMapper to work this way with
Current Jackson versions:
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
Older Jackson versions:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
From the Jackson Annotations documentation:
// ensure that "id" and "name" are output before other properties
#JsonPropertyOrder({ "id", "name" })
// order any properties that don't have explicit setting using alphabetic order
#JsonPropertyOrder(alphabetic=true)
The following 2 ObjectMapper configuration are required:
ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
or
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
defines the property serialization order used for POJO fields
Note: does not apply to java.util.Map serialization!
and
ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
or
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
Feature that determines whether java.util.Map entries are first sorted by key before serialization
Spring Boot config example (yaml):
spring:
jackson:
mapper:
SORT_PROPERTIES_ALPHABETICALLY: true
serialization:
ORDER_MAP_ENTRIES_BY_KEYS: true
In Spring Boot you can add this behaviour globally by adding the following to your Application entry point class:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
In Jackson 2.x, which you are probably using today, use:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
If you care about looks, you may also consider SerializationFeature.INDENT_OUTPUT as well.
Note that you must serialize Maps or Objects for this to sort correctly. If you serialize a JsonNode for example (from readTree), that won't be properly indented.
Example
import com.fasterxml.jackson.databind.*;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
String input = "{\"hello\": {\"cruel\" : \"world\"} }";
Object pojo = mapper.readValue(input, Object.class);
System.out.println(mapper.writeValueAsString(pojo));
results in:
{
"hello" : {
"cruel" : "world"
}
}
There is an easier way in Spring Boot by specifying a property (in application.properties for example:
spring.jackson.mapper.sort_properties_alphabetically=true
From Duncan McGregor's answer:
Its better to use it like this:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
as MapperFeature is for XMLs and comes with jackson-databind which is not required...
I discovered yet another way today in case alphabetic is not your desired sorting order. It turns out adding a #JsonProperty annotation on a field places it last when writing if the rest of the fields are not annotated. I discovered that when I wanted to specify a property name which did not conform to java naming conventions.
By Adding an index attribute you can define the order. Lowest index is placed first.
#JsonProperty(index=20)
String prop1;
#JsonProperty(index=10)
String prop2;
Would render:
{"prop2": "valueProp2", "prop1": "valueProp1"}
You can use mix-in and specify the order of properties as you like:
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
public final class ObjectMapperUtils {
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.addMixIn(Object.class, IdFirst.class);
}
#Bean
public ObjectMapper objectMapper() {
return MAPPER;
}
#JsonPropertyOrder({"id", "...", "..."})
private abstract static class IdFirst {}
}
I realize this is an old thread, but since I was looking or an answer and landed here, some additional info could be handy for other people.
The #JsonProperty annotation I am using currently (jackson-annotations-2.11.2) accepts, besides the "value" argument, an "index" (numeric) argument that specifies the order of the fields during serialization.
Instead of using flag argument:
objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
As #Gary Rowe mentioned, we can use Jackson2ObjectMapperBuilder to sort the properties globally.
However for this to work, you must have Jackson2ObjectMapperBuilder in your classpath. It is not part of the Jackson library.
As per this documentation, spring-web dependency has Jackson2ObjectMapperBuilder file and should be in your classpath.
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
You can refer to this for other possible solutions

jackson map serialization, custom serializer for the key do not invoked

I need to have functional which allow me to serialize Map<CustomType1, CustomType2>.
I create custom Serializer inherited from JsonSerializer.
I also create simple module and register it in my mapper;
SimpleModule myModule = new SimpleModule("myModule");
myModule.addKeySerializer(CustomType1.class, new CustomType1Serializer());
myModule.addSerializer(CustomType1.class, new CustomType1Serializer());
mapperInstance.registerModule(myModule);
And when I just serializing an instance of CustomType1 it works perfectly, but when I creating map and trying to serialize it, than jackson skip my serializer and using StdKeySerializer. How to fix that???
Thanks for your attention.
This problem seems related to Jackson's handling of generic objects. One way to get around the issue is by using a super type token to strictly define the map type. Illustrated:
final ObjectMapper mapper = new ObjectMapper();
final SimpleModule module = new SimpleModule("myModule",
Version.unknownVersion());
module.addKeySerializer(CustomType1.class, new CustomType1Serializer());
mapper.registerModule(module);
final MapType type = mapper.getTypeFactory().constructMapType(
Map.class, CustomType1.class, CustomType2.class);
final Map<CustomType1, CustomType2> map = new HashMap<CustomType1, CustomType2>(4);
final ObjectWriter writer = mapper.writerWithType(type);
final String json = writer.writeValueAsString(map);
addSerializer and addKeySerializer are just two types of available serializers that deal only with simple, non-POJO types. To have custom serialization for more complex types such as maps and collections, you need to .setSerializerModifier on your module, with a BeanSerializerModifier that overrides the modifyMapSerializer method and returns your custom serializer

Categories