How to modify spring/jackson JSON deserialisation - json

I'm trying to figure out how to adjust the way spring/jackson convert a JSON string (stored in a file) into various POJOs. For example, if I have this JSON:
{
"rates":{
"EURUSD":5.4321,
"USDHKD":1.2345
}
}
I actually want to get an instance of my 'Rates' class. Inside that I want a List containing each individual rate.
In my spring config file I created this entry:
#Bean
public ObjectMapper jsonObjectMapper() {
return new MappingJackson2HttpMessageConverter().getObjectMapper();
}
And in my service class I did this:
#Autowired
ObjectMapper jsonObjectMapper;
public Rates currentRates() {
Resource resource = this.ctx.getResource("classpath:stub/data/rates/Rates-01.json");
return this.jsonObjectMapper.readValue(resource.getURL(), Rates.class);
}
The problem is that I am trying to figure out how to take the Map containing the currencies as a single key, break those currencies in two and then create a RateEntry object containing the two currencies and the rate, before populating a list in the Rates class.
I've been looking at Spring's Conversion Service with the idea to define a converter that maps the Map to a list. i.e. this signature: Converter<Map<String, BigDecimal>, List<Rate>>. However this is based on the assumption that the JSON is first converted to standard types before the conversion service is called. An assumption I now think is incorrect.
So I'm now trying to figure out if I need to register some sort of custom ObjectMapper to handle reading directly from the JSON String data. But that sounds like over kill as I only want to adjust part of the object graph, and let the default converters handle the rest.
Any pointers appreciated. Thanks.

Ok, Jackson tries to stay away from structural transformations (since it's a quick-sand pit with unlimited number of general permutations). But it might be possible to use some existing features to do what you want.
First: to use Object key to indicate type, you will probably want to enable polymorphic type handling with "as object wrapper" inclusion.
So add something like:
#JsonTypeInfo(as=Include.WRAPPER_OBJECT)
for your Rates class declaration.
As to converting values into list; this might work by defining "any-setter" (see http://www.cowtowncoder.com/blog/archives/2011/07/entry_458.html), something like:
#JsonAnySetter
public void set(String key, Double value) // or "Object value")
{
list.add(new Rate(key, value));
}
I hope this helps.

Related

Unity JSONUtility to JSON list of base classes

I have a BaseClass and bunch of derived classes.
I also have List<BaseClass> that contains objects from those derived classes.
When I do JSONUtility.ToJson(List<BaseClass>) I get only properties of BaseClass and not derived classes.
And well... I guess it is logical, but can't I force it to use derived class if there's a one or JSONUtility isn't capable of it? So I need to write custom logic for that?
Thanks!
Very probably JSONUtility.ToJson(List<BaseClass>) gets the elements you need with reflection, so the object returned is based on the incoming type.
I would try to obtain the jsons one by one and combine them in the logic, pre casting each of the types. Not tested nor debugged, just an starting point idea to move on:
string jsons;
foreach (var baseClass in baseClassList) {
Type specificType = baseClass.GetType();
string jsonString = JsonUtility.ToJson((specificType)baseClass)
jsons = "[" + string.Join(",", jsonstring) + "]";
}
I faced the same issue, to be honest JsonUtility is not good option for working with List.
My recommendations:
Use array instead of list with this helper class
or Newtonsoft Json Unity Package
I also needed JSON serialization, to call a REST json API, and I suggest to avoid JSONUtility.
It doesn't handle lists or dictionaries, as you saw.
Also it cannot serialize properties defined with { get; set; }, only fields, which is not blocking but not very convenient.
I agree with the recommendation above, just use Newtonsoft. It can serialize anything, and you will also benefit of the Serialization Settings (you can for example setup the contract resolver to convert all property names to snake_case...). See https://www.newtonsoft.com/json/help/html/SerializationSettings.htm

Create list of custom objects from flat JSON data

I want to create an arraylist of type Adapter from a JSON. But since the JSON is not in arraylist format, I'm unable to use gson.fromJson() method.
Is there any way by which I can create a list of my custom object by parsing the following JSON?
JSON data:
"source":{"adapter-config.adapter[0].name":"testAdapter1",
"adapter-config.adapter[0].resolverName":"serviceResolver",
"adapter-config.adapter[0].parameters[0].key":"serviceId",
"adapter-config.adapter[0].parameters[0].value":"serviceIdPathInEvent",
"adapter-config.adapter[0].parameters[1].key":"appId",
"adapter-config.adapter[0].parameters[1].value":"appIdPathEvent",
"adapter-config.adapter[0].parameters[2].key":"env",
"adapter-config.adapter[0].parameters[2].value":"envPathInEvnet"}
My Adapter Object:
public class Adapter {
private String name;
private String resolverName;
private List<KeyValuePair<String, String>> attributeList;
}
Gson does not provide such functionality out of the box. However you can achieve this by manually reading the JSON data from a JsonReader, consuming the JSON property names with nextName() and then parsing them to determine which data they represent. You could either directly read from a JsonReader, or in case the shown JSON data is only an extract from a larger JSON document, you can implement a TypeAdapter for your List<Adapter>. That TypeAdapter could then either be registered with a GsonBuilder by providing new TypeToken<List<Adapter>>() {}.getType() as type, or you could annotate the field holding the List<Adapter> with #JsonAdapter.
For the actual parsing of List<Adapter>, I would recommend storing a current adapter (and its index in the list) in a local variable. Whenever you parse a JSON property name, you could then check if the index encoded in the name is equal to the index of the current adapter, then you are going to modify the existing instance, otherwise if the encoded index is equal to the index of the current adapter + 1 you create a new Adapter instance, add it to the list of adapters and reassign the current adapter variable and its index variable. Then you continue with parsing the remainder of the property name to find out which Adapter field values to set.
(In case you get stuck there, feel free to let me know in the comments and I can try to provide some concrete code; but it would probably be best if you tried it yourself first.)

Saving and loading with Json

Until now I was using serializable for saving and loading data, but now I have switched to JSON. And as soon as I have started there is a problem. Json will not save me any class. I was trying to save standalone Variables.class. This class contains various static data - player type string, bonus type strings, scores. Then I did create private class in class, then class in method, but nothing. Output od System.out.println(json) is always just {}.
For example this was my last attempt to make things work:
public static void saveAVD() {
Variables v = new Variables();
String json = new Json().toJson(v);
System.out.println(json);
file.writeString(json.prettyPrint(json), false);
}
Note: File variable is located in local bin folder. Inside that file are also just two empty bracelets ({})
Any advice?
(Credit to noone, who answered this in a comment).
JSON will, by default, not serialize static fields. The quickest way to get the behavior you want would probably be to convert the Variables class into a singleton.

Jackson parser handling of multiplefield names with same name

I see a feature of Jackson JSON parser as an inherent problem for my case.
I am to parse an unknown file which might not comply with the json formats, thereby end up having multiple key names that are same. In that case, if i call a function like getFieldNames() on it , it ends up giving only one entry among those multiple same simple elements.
So if i do a get(String) on it, i'll end up getting only one of those Json nodes having the same key value where as i'm supposed to get all the others
Any comments or solutions on this?
Most JSON parsers will reject your input file out of hand, as duplicate keys at the same nesting level are not allowed (this is a de-facto standard). However, certain parsers will allow you to handle the duplicate in a variety of ways.
One way to handle this in Jackson, would be to map regular attributes into an entity class, then handle the potential duplicates via a #JsonAnySetter.
public class Bag {
final transient Multimap<String, Object> multimap = LinkedListMultimap
.create();
// regular properties, constructors etc
#JsonAnySetter
public void add(final String key, final String value) {
multimap.put(key, value);
}
}
Note the use of a multimap: regular hash maps cannot contain duplicate keys, so a multimap is a requirement for a working solution. After deserializing the input file, all 'regular' JSON attributes will be mapped to their corresponding entity properties, whereas all duplicates will be stored in the map, and available for manual processing.
final List<Object> duplicatedValues = multimap.get(someKey);
Alternatively, you could create a custom deserializer, which will recieve all tokens (regardless of wether they are duplicates or not).

one Jackson deserializer for multiple types (config by annotation)

I'm trying to change the (de)serialization of a list in one of my classes.
the objects in the list shall be serialised as int (their jpa id) and deserialised accordingly. serialization is simple.
for the deserialization i have a class that can translate the id into the object if id and class are known.
How do i get the necessary class from jackson? all default jackson serialisers have a constructor like this: protected StdDeserialiser(Class<?> vc) so the information is present somewhere.
is there a way to access it during deserialisation?
or before the deserialiser is constructed by jackson?
or inside the HandlerInstantiator?
I only want to overwrite the default deseriliser for certain references so i can't just write a provider or a custom module.
I made it work from inside the deserializer with the help of the ContextDeserializer interface as this supplies the deserializer with the target property.
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
Class<?> vc = null;
if (property.getType().isCollectionLikeType()) {
vc = property.getType().getContentType().getRawClass();
} else {
vc = property.getType().getRawClass();
}
return new ResourcePathDeserializer(vc, converter);
}
This solution is not perfect as I only get the raw class of the return type or the generic (which might be a parent class or an interface) but that is enough for my requirements.
It would be better if I could access the "real" class that was resolved by Jackson, but for me this works.
First of all, there is nothing fancy about writing a Module: it is just a way for plugging things in, like custom (de)serializers. So no need to avoid that. And you will most like need to write a module to do what you want.
In general it is not a good idea to try to create "universal" serializers or deserializers, and it will probably run into problem. But it depends on what exactly you are trying to do.
Type information will either be:
Implicit from context: you are writing a (de)serializer for type T, and register it for it, so that's your type
Passed by Jackson when (de)serializer is being constructed, via Module interface: modules are asked if they happen to have a (de)serializer for type T. SimpleModule will only use basic Class-to-impl mapping (that's where "simple" comes from); but full custom Module has access to incoming type.
But I don't know if above will work for your use case. Type information must be available from static type (declared content type for the list).