How does XStream select its converters? - java

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());

Related

Using Jackson Mixins to selectively ignore only provided properties

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.

JSON Binding #JsonbTypeDeserializer annotation ignored on enums?

I'm converting a JAXB application to JSON-B and I've run into an issue while trying to deserialize a Java enum using a custom JsonbDeserializer inside one of my tests.
The original JSON I need to deserialize contains ints referencing the enum's constants. Therefore my custom JsonbDeserializer needs to take the int and return the enum constant with the matching ordinal. It looks like this:
#JsonbTypeDeserializer(Region.RegionDeserializer.class)
public enum Region implements BaseEnum {
REGION_A,
REGION_B;
static final class RegionDeserializer implements JsonbDeserializer<Region> {
// deserialize() method returns REGION_A for 0 and REGION_B for 1.
}
}
Then I run it like this:
try (var jsonb = JsonbBuilder.create()) {
var result = jsonb.fromJson(text, Region.class);
} catch (final Exception ex) {
fail(ex);
}
Unfortunately, here's what I get back:
java.lang.IllegalArgumentException: No enum constant Region.1
at java.base/java.lang.Enum.valueOf(Enum.java:266)
at org.eclipse.yasson.internal.serializer.EnumTypeDeserializer.deserialize(EnumTypeDeserializer.java:40)
As you can see, RegionDeserializer is not used. Instead, the default enum deserializer is used. Looking into the JSON-B docs, I see I should register the deserializer manually:
JsonbConfig config = new JsonbConfig()
.withDeserializer(RegionDeserializer.class);
Jsonb jsonb = JsonbBuilder.create(config);
...
And when I do that, the code in fact works. But here's my question - what can I do to have the JsonbTypeDeserializer annotation registered automatically? Considering I have a lot of enums I need custom deserializers for, registering them manually really doesn't scale.
EDIT 1: I have tried to use #JsonbCreator-annotated static method instead, and the result was the same. The default enum deserializer was still used.
The JSON-B specification mentions both ways of registering the custom deserializer:
There are two ways how to register JsonbSerializer/JsonbDeserializer:
Using JsonbConfig::withSerializers/JsonbConfig::withDeserializers method;
Annotating a type with JsonbSerializer/JsonbDeserializer annotation.
The fact that the annotation does not work is a bug. I could reproduce this on Yasson 1.0.6, but not on Yasson 2.0.0-M1. Perhaps updating to the latest version solves your problem?

Java Webclient: How to serialize to Pascal JSON?

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

How to get a specific custom converter from DozerBeanMapperFactoryBean?

I am working on a particular task which consisters of different phases. In the first phase I created a class(a) that reads a property file and saves the data into a hashmap. This class has a method (1) that will get the key and return the corresponding value from the map, if there is no entry in the map the key enter will be returned.
In the second phase I created a custom converter class (b)the extends the DozerConverter class I then autowired class (a) and in the method convertTo() that is provided by the DozerConverter class I simpley call the method (1) of class (a).
Since I am using Spring I have used the following code
#Bean
public DozerBeanMapperFactoryBean configDozer() throws IOException {
DozerBeanMapperFactoryBean mapper = new DozerBeanMapperFactoryBean();
mapper.setCustomConverters(Collections.<CustomConverter> singletonList(new ErrorCodeConverter()));
return mapper;
}
The part that I am confused about is how can I get the ErrorCodeConverter from the mapper object and use this class which is my custom converter. I have found lots of examples where they provide xml mappings but I don't want any xml mapping. This job is being done for me by class (a). I have wrapped class (a) with custom converter to make using of dozzer mapper api.
Any help will be highly appreciated.
I just take a look in Dozer code from my local (5.3.1), it appears Dozer is designed to apply Custom Converter at 'field' level not 'class' level through API.
You can tweak a bit which wrap your object as a field of another class so you can use API to perform CustomConverter. Hope it helps.
Nghia

How can I register the Scala Jackson Module in Riak-Java?

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

Categories

Resources