I am trying to interface with another system that is has extremely specific integration parameters. They don't have any code written to ignore case sensitivity and, long story short, for a post request I am trying to make, they are expecting a JSON body with field names in Pascal case instead of Camel Case and the request fails without Pascal. We are using WebClient to send integration calls so we can support reactive flows in our code. As far as I've been able to tell, when I use WebClient to serialize to JSON, the request is being converted to use Camel Case, which I would normally want.
How can I serialize this to Pascal instead? Everything I try to research about this ends up landing me in .NET land, but I'm not writing this in C#. I'm writing it in Java.
//For example:
{"originTypeCode":"US","camelCaseFieldName":"FAILED"} // FAILURE
{"OriginTypeCode":"US","PascalFieldName":"SUCCESS"} // SUCCESS
I have two ideas:
1) This seems less ideal, but perhaps more intuitive. The idea is to convert the object I'm trying to post to JSON first, then with a parser convert all the fields from Camel Case to Pascal, then try and post that with my WebClient method. This doesn't seem like the most ideal way to do this. I'd imagine there is probably something a lot cleaner.
2) The second idea is that my WebClient instance serializes using a Jackson serializer. I think if I were to create a new Bean of WebClient/Jackson ObjectMapper, maybe I can write a custom converter to use specifically for this integration flow. This seems like it is perhaps cleaner, but digging through WebClient and it's build methods, it's difficult to figure out how to accomplish this. Below I'm posting the beans as I have them currently defined. Digging into this kind of thing is pretty new to me, so I'm not sure what would need to be changed or where. The WebClient bean is from a WebClientConfig class and the ObjectMapper is from my JacksonConfig class.
#Bean
public WebClient webClient() {
return WebClient.builder().clientConnector(getClientHttpConnector()).build();
}
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
I am definitely open to other suggestions as well.
The comments from #GriffeyDog have helped me figure out what I need to do. For anyone that stumbles across this, the solution was the following:
If you want certain classes to serialize in specific ways, you can annotate the class itself with the annotation #JsonNaming, and then specify a naming strategy, a list of which can be found here: https://java-focus.com/jackson-property-naming-strategy/. For my use case, I used PropertyNamingStrategy.UpperCamelCaseStrategy.
#JsonNaming(value = PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public class MyPascalSerializedClass {}
//All fields in this class will serialize to "UpperCamelCase" instead of "normalCamelCase".
Additionally, if you wanted to specify certain fields, you can use the #JsonProperty annotation to override even the class annotation. For example, I had a field within my class that had to map to a JSON format that didn't fit to any standard convention, so I was able to use this.
#JsonProperty("ULDNumber")
private String uldNumber
//This field will serialize to the specified "ULDNumber".
This is all part of the com.fasterxml.jackson library. For further documentation you can refer to the link above and the following: https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/PropertyNamingStrategy.UpperCamelCaseStrategy.html
Related
I have the following situation:
public class A {
private String someProperty;
private String anotherProperty;
public A() {}
// getter/setter
An ObjectMapper configuration as follows (enabled by default, but worth noting to get the point of the question across):
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
and an input JSON that looks as follows:
{
"someProperty": "someValue",
"anotherProperty":"anotherValue",
"unwantedProperty":"unwantedValue"
}
When deserializing this JSON using objectMapper.readValue(bytes, A.class), as expected, it gives me an exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "unwantedProperty"
Now, what I want to do, is the following:
Only deserialize properties that are explicitly provided by A.class
Use a MixIn to ignore all unwanted, known properties. So for example, I know that unwantedProperty is part of the JSON, but I don't need it, so I want to ignore it.
Still raise an UnrecognizedPropertyException if a new, unknown property suddenly appears in my JSON.
The reason that I wish to use something like a MixIn class for this is that in reality, the input JSON has several dozens of fields. I would prefer not to clutter my A.class with dozens and dozens of unused properties with #JsonIgnore on them, so that it only contains the fields that I really want. If a new property unexpectedly does come along, I want to be forced to have a look at it.
I thought that I could allow this behaviour by using a MixIn as follows:
public abstract class AMixIn {
#JsonIgnore private String unwantedProperty;
together with:
objectMapper.addMixIn(A.class, AMixIn.class);
but this seemingly has no effect. I've also tried creating getters in AMixIn and giving those #JsonIgnore, but this also has no result.
Am I using MixIns incorrectly here? Is what I'm trying to do even possible (as described in the 3 points above)? Or, is there a better way to do this?
I've tried searching, but my use case is a bit esoteric, so I haven't had much luck.
Answer to question 1:
You can instruct Jackson to ignore unknown properties.
I tend to configure the ObjectMapper to ignore them,
here is some sample code:
private ObjectMapper mapper;
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// This matches the Fuse Mapper configuration.
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
mapper = builder.build();
You can also annotate the class to ignore unknown properties.
Here is some sample code:
#jsonignoreproperties(ignoreunknown = true)
public class A
{
...
Note about question 2:
In order for the use-mixin-to-ignore-fields-in-json strategy to work,
the fields to be ignored must exist in the class.
In your case,
this means that class A must have a field "unwantedProperty" for the mixin to work correctly.
Direction to solve questions 2 and 3
You cannot use the MixIn feature of Jackson to solve either
question 2 or question 3.
Instead,
you will need to write a custom deserializer.
I suggest that you also use a custom Jackson annotation
that configures a list of ignored-unrecognized-fields
and have your custom deserializer only throw the exception
for unrecognized fields that are not part of the
ignored-unrecognized-fields list.
Well, shows how well I can search; as always, Jackson provides some way of doing whatever needs doing. In case it might help someone else:
There exists the #JsonIgnoreProperties annotation which, in addition to the ignoreUnknown property (that I already knew), supports a list of properties (via value()) to ignore during de-/serialization (which I did not know).
This is not quite the same as the intended solution above, but having these properties inside of #JsonIgnoreProperties({ ... }) in the class header instead of the class body is a good enough compromise for me.
So, the solution would be:
#JsonIgnoreProperties({"unwantedProperty"})
public class A {
// same as above...
}
Still, if there is a MixIn solution which can completely decouple these things, I'd still like to see it. I'll accept my own answer if nothing comes up in a few days.
Explanation & Workaround
Currently I am using JAX-RS and letting JAXB bindings automatically handle converting the data to XML and JSON for me in a JEE6 project. Everything is working absolutely fantastically until I try to create a generic response object to wrap all of my information in.
When I attempt to use my generic response class com.eln00b.Wrapper (which contains a private Object result attribute within it) I get:
javax.xml.bind.MarshalException - with linked exception: [com.sun.istack.SAXException2: class com.eln00b.CustomObject nor any of its super class is known to this context. javax.xml.bind.JAXBException: class com.eln00b.CustomObject nor any of its super class is known to this context.]
So I add to com.eln00b.Wrapper:
#XmlSeeAlso ({com.eln00b.CustomObject})
public class Wrapper {
}
Everything works fine.
The Problem
I want this to be extremely generic. I do not want t constantly add classes to the #XmlSeeAlso annotation on the com.eln00b.Wrapper class. How do I have the system automatically locate all of my classes for the JAXB context?
Even if it's a hack where I use something like Reflections to load the data, that's fine. I'm just not sure how to get the context to load all of that data without the #XmlSeeAlso annotation. With the large amount of annotations I will be creating it will just simply not work.
How It Worked Manually
It worked manually just by adding the data like so doing manual conversions. However, I do not want to use manual XML/JSON creation unless I absolutely need to (I don't want to deal with content negotiation or anything like that).
Sample:
JAXBContext.newInstance(new Class[] {Wrapper.class, CustomObject.class});
So here is what the essence of the custom resolver looks like:
#Provider
#Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class JaxbContextResolver implements ContextResolver<JAXBContext> {
#Override
public JAXBContext getContext(Class<?> type) {
// load appropriate context data
Class[] bindTypes = ...
// create
try {
return JAXBContext.newInstance(bindTypes);
} catch (JAXBException e) {
// todo: this can be handled better but works for the example
throw new RuntimeException(e);
}
}
}
Now, the processing for "load appropriate context data" is pretty simple. By basically mimicking #XmlSeeAlso using runtime data:
Create a custom something (annotation, processing method, whatever) that marks a particular field/method as "contextual"
Load the field/method data pulling the data types out
Make sure you do not load duplicates and check for infinite recursion possibilities
Now, I used some caching to help make things more efficient for myself. I also created a slightly more complex setup for my root object where it actually kept track of the class data on its own and made it pretty speedy. I also created an alternative that marked classes as "contextual" that I used package inspection to load via annotations and just automatically add to the context but I have not checked efficiency on that yet. I have some ideas for a 3rd implementation, but I want to get more benchmarking completed.
Consider the following interface/object hierarchy in a spring project:
public interface MyInterface {
//method defenitions
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class MyClass implements MyInterface {
//method implementations
}
I use MyClass in a controller method where it is read from the requests body:
#RequestMapping(method = POST, value = "/posturi", consumes = "application/json")
public void createEntity(#RequestBody MyClass myClass) {
//handle request
}
The jackson library is used to read json data and convert it to a java object.
I would like to change the type of the parameter in the controller method from MyClass to MyInterface. This does not seem to work since the interface can't be instantiated with the new operator. But it could be created like this:
MyInterface instance = applicationContext.getBean(MyInterface.class);
Is it possible to make spring/jackson instantiate the object this way? I would like to do this so that my controller does not need to be aware of what implementation is used.
It should be possible with Converters. See documentation http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html. Question is, how do you know which class you return by converter? Rather rethink your design to use POJOs in input.
I have solved this now and the concept is quite simple but the implementation can be a bit tricky. As I understand it, you can annotate any type with #RequestBody as long as you provide a HttpMessageConverter that can convert from a http request to your desired type.
So the solution is:
Implement a HttpMessageConverter
Configure spring so that your HttpMessageConverter is used.
The second part can be a bit tricky. This is because spring adds a bunch of default HttpMessageConverter that can handle common types such as strings, integers, dates and I want these to continue to function as usual. Another problem is that if jackson is on the path, spring also adds a MappingJackson2HttpMessageConverter for generic json handling such as converting to concrete objects, maps and so on. Spring will use the first HttpMessageConverter it finds that claims to be able to convert to your type. The MappingJackson2HttpMessageConverter claims to be able to do so for my objects, but it is not able to, so it fails and the request fails. This could be considered a bug...
The chain that I wanted was:
Springs default HttpMessageConverters.
My own HttpMessageConverter
The MappingJackson2HttpMessageConverter
I found two ways to acheive this. First, you can declare this explicitly through xml.
<mvc:annotation-driven>
<mvc:message-converters>
<!-- All converters in specific order here -->
</mvc:message-converters>
</mvc:annotation-driven>
The downside of this is that if the default HttpMessageConverter chain changes in later releases, it will not change for your configuration.
Another way to do it is to programatically insert your own HttpMessageConverter before the MappingJackson2HttpMessageConverter.
#Configuration
public class MyConfiguration {
#Autowired
private RequestMappingHandlerAdapter adapter;
#Autowired
private MyHttpMessageConverter myHttpMessageConverter;
#PostConstruct
private void modify() {
List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
int insertLocation = messageConverters.size() - 1;
for (int i = 0; i < messageConverters.size(); i++) {
Object messageConverter = messageConverters.get(i);
if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
insertLocation = i;
}
}
messageConverters.add(insertLocation, myHttpMessageConverter);
}
}
The second alternative will continue to use the "default configuration" even if it changes in later releases. I consider it a bit hacky and not at all elegant but the reason I think it is a valid soulution is that there seems to be flaws in the MappingJackson2HttpMessageConverter claiming to be able to convert to types it cannot convert to. And also that you cannot explicitly add a HttpMessageConverter to a specific position in the chain.
For now I am going with the second option but how you do is up to you...
Needed to have support for Scala Lists and Options when storing to Riak. Looks like the Scala Module for Jackson would work for this. However, how would it be hooked into the object mapper in Riak? Not sure if I need to override something or if there's an annotation that can easily solve it.
https://github.com/FasterXML/jackson-module-scala
A few releases ago I added a getObjectMapper() method to the JSONConverter as someone had requested it. This is the Converter that is used for fetch/store operations if you're passing in POJOs and haven't created and passed in your own.
You'd want to instantiate the JSONConverter yourself and get the ObjectMapper from it:
JsonConverter<MyClass> converter = new JSONConverter<MyClass>(MyClass.class, bucketName);
ObjectMapper om = converter.getObjectMapper();
You can now register the module with the ObjectMapper and then use the JSONConverter with your fetch/store operations (using the withConverter() method of the StoreObject and FetchObject).
I think that's what you're looking for. If you needed more control over serializing/deserializing your objects you could also write your own Converter- I've written a cookbook entry on the subject here: https://github.com/basho/riak-java-client/wiki/Using-a-custom-Converter
The only documentation on XStream converters that I can find is on these two pages:
Tutorial
List of all shipped converters
When XStream is parsing XML input, it uses a ConverterLookup (and by default, a DefaultConverterLookup) to lookup which converter to use by class. I'd like to configure my XStream mapper to use my own custom ConverterLookup, but only see a getConverterLookup() method, not a respective setter.
I have an instance where XStream is encountering a Date value in the XML, and returning using the respective DateConverter. I want it to use a different converter, which (I believe) means I need to set/register my own Converter impl. Just can't figure out how to do this. Thanks in advance.
First of all your question is in fact two unrelated questions, I'll try my best to answer them both.
Converters
To your second question regarding date conversion. Which in my mind seems to be the reason why you are here.
The basic way of adding your own converter is rather simple, the method registerConverter should give your a clue. If you are wondering how to implement a Converter I suggest you take a look at one of the many converters already provided by XStream. On an extra note I feel like I must mention the priority of converters.
The converters can be registered with an explicit priority. By
default they are registered with XStream.PRIORITY_NORMAL. Converters
of same priority will be used in the reverse sequence they have been
registered. The default converter, i.e. the converter which will be
used if no other registered converter is suitable, can be registered
with priority XStream.PRIORITY_VERY_LOW. XStream uses by default the
ReflectionConverter as the fallback converter.
In other terms, given two converters accepting the same classes, the one who was added last will be used.
ConverterLookup
To answer how you can use your ConverterLookup there are two ways which may yield the same results, personally I would go for alternative 2.
1) Overriding getConverterLookup
XStream xs = new XStream(){
#Override
public ConverterLookup getConverterLookup() {
return new ConverterLookup() {
public Converter lookupConverterForType(Class type) {
//Do your magic here
}
};
}
};
2) Using a Mapper
In this case I would keep the DefaultMapper and instead implement MapperWrapper's for my new mappings. (Have a look at buildMapper inside of XStream.java to see some of the defaults) Initialize like this:
ClassLoader classLoader = new ClassLoaderReference(new CompositeClassLoader());
Mapper mapper = new DefaultMapper(classLoader);
mapper = new MyOwnMapperWrapper(mapper);
XStream xs = new XStream(null, mapper, new XppDriver());