spring boot json to model conversion error - json

{
"cust":"A",
"del":[{
"type1": "id",
"type2":[{
"name":"address"
}]
}]
I have converted this json to below model class
public class Del{
public String type1;
public JSONArray type2; // "type2" has dynamic key and value which are string, it can have "name":"address","id":"sal" pair etc dynamically
}
public class Root{
public String cust;
public List<Del> del;
}
But, this mapping is showing error. how to fetch key and value inside "type2" in dynamic way where attribute names are not fixed.

If you have a list, you need a List<T>. What is T? If it can have any number of keys, then it must be a Map<K, V>. K is String, and V is either String or Object, depending on your actual structure. If you have a handful of expected keys and want to ignore everything else, make T a custom class of yours:
class Type2Props {
public String name;
public String address;
public String id;
public String sal;
}
Properties/fields without a value in the JSON will be left initialized as null.
Putting it together, you either need
public class Del {
public String type1;
public List<Type2Props> type2;
}
or
public class Del {
public String type1;
public List<Map<String, String>> type2; /* might be `Map<String, Object>` */
}

Related

Spring - How to convert a String representation of Date in JSON body field in request model comming into controller to OffsetDateTime

I have legacy data coming in to my API as part of UserRequest model like
#PostMapping
public MyResponse saveUser(#Valid #RequestBody UserRequest userRequest) {
...
}
UserRequest class uses OffsetDateTime for dateRegistered field:
public class UserRequest {
...
OffsetDateTime birthDate;
...
}
The problem I am having is that the data is coming into the API using below format for dateRegistered field:
{
"last-name":"Mikel",
"birth-date":"20200716"
}
So, the string representation "20200716" from JSON request needs to be somehow converted to OffsetDateTime like "2020-07-16T00:00:00Z" (the time portion and offset is set to 0s).
By default, the "20200716":
{
"last-name":"Mikel",
"birth-date":"20200716"
}
, gets converted to OffsetDateTime value of
{
"last-name": "Mikel",
"birth-date": "1970-08-22T19:18:36Z"
}
, which is obviously way off.
How do I convert a string representation of date in Json field like "20200716" to its OffsetDateTime representation like "2020-07-16T00:00:00Z" or "2020-07-16T00:00:00.000+00:00"?
I was trying to annotate my OffsetDateTime field with #JsonFormat("yyyyMMdd") but that is throwing exception like: JSON parse error: Cannot deserialize value of type java.time.OffsetDateTime from String "20200716".
you don't need a JSON annotation. You need to adjust the setter as follow.
public class MedicalCandidateRequest {
private OffsetDateTime dateRegistered;
public OffsetDateTime getDateRegistered() {
return dateRegistered;
}
public void setDateRegistered(String dateString) {
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSxx";
DateTimeFormatter dtFormatter = DateTimeFormatter.ofPattern(pattern);
this.dateRegistered = OffsetDateTime.parse(dateString, dtFormatter );
}
}
Change the parameter of the setter method to a String and do the conversion yourself.
public void setDateRegistered(String value) {
this.dateRegistered = doConversionHere(value);
}
Thanks for suggestions but I have decided to go with my own implementation.
I provided a custom deserializer like:
public class CustomDateDeserializer extends JsonDeserializer<OffsetDateTime> {
private static final String PATTERN = "yyyyMMdd";
private final DateTimeFormatter formatter;
public CustomDateDeserializer() {
this.formatter = DateTimeFormatter.ofPattern(PATTERN);
}
#Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JacksonException {
LocalDate localDate = LocalDate.parse(parser.getText), formatter);
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDate, LocalTime.MIDNIGHT, ZoneOffset.UTC);
return offsetDateTime;
}
}
, which I then use to annotate my model field like:
#JsonDeserialize(using = CustomDateDeserializer.class)
private OffsetDateTime birthDate;

How to combine #JsonView with #JsonProperty?

I have a DTO class that should serve json via a spring-mvc #RestController.
I want to provide different version/views on the same object. Especially, there are fields that are only used in VERSION_1 of the api, and some only in VERSION_2.
Problem: I could add #JsonView for this, but my goal is also to rename those fields. Some fields should actually replace the same name from previous versions.
Example:
public class Person {
#JsonView(View.Version_1.class)
#JsonProperty("name")
private String name; //eg only the firstname
#JsonView(View.Version_2.class)
#JsonProperty("name")
private NameDTO namedto; //now changing to first+last name
static class NameDTO {
private String firstname;
private String lastname;
}
}
#RestController
public class MyServlet {
#GetMapping("/person/{id}")
#JsonView(View.Version_1.class)
public PersonDTO person1(int id) {
//...
}
#GetMapping("/person_new/{id}")
#JsonView(View.Version_2.class)
public PersonDTO person2(int id) {
//...
}
}
So, depending on the view/version, you would get the same json field firstname, but with different content.
In this example, using V1 would give:
{"name": "john"}
Whereas using V2 should result in:
{"name": {"firstname": "john", "lastname": "doe"}}
BUT not with he code above, as jackson complains:
com.fasterxml.jackson.databind.JsonMappingException: Conflicting
getter definitions for property "name".
Is that possible at all?
I found a way using:
https://github.com/jonpeterson/spring-webmvc-model-versioning
Basic idea is to add a custom VersionedModelConverter that is applied on #VersionedModelConverter annotated webservice response classes.
#Configuration
#Import(VersionedModelResponseBodyAdvice.class)
public class SpringMvcVersioningConfiguration {
//register in jackson. spring-boot automatically registers any module beans
#Bean
public Model versioningModel() {
return new VersioningModule();
}
}
#GetMapping
#VersionedResponseBody(defaultVersion = "2.0")
public Person person() {
}
#JsonVersionedModel(currentVersion = "3.0" toPastConverterClass = PersonConverter.class)
public class Person {
}
public class PersonConverter implements VersionedModelConverter {
#Override
public ObjectNode convert(ObjectNode modelData, String modelVersion, String targetModelVersion, JsonNodeFactory nodeFactory) {
Double modelv = Double.valueOf(modelVersion);
Double targetv = Double.valueOf(targetVersion);
//todo if-else based on model version
Object node = modelData.remove("fieldname");
//node.change...
modelData.set("fieldname_renamed", node);
}
}

Deserialize this json in VB.NET

I have this json response:
{
"tracked_until": "1483704963",
"solo_competitive_rank": "4066",
"competitive_rank": "3821",
"mmr_estimate": {
"estimate": 3971,
"stdDev": 215.26495302301302,
"n": 20
},
"profile": {
"account_id": 131505839,
"personaname": "LeG",
"name": null,
"cheese": 1,
"steamid": "76561198091771567",
"avatar": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/c0/c09ca9b316ff7bf7dccba6f5a32aba97b8dba05c.jpg",
"avatarmedium": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/c0/c09ca9b316ff7bf7dccba6f5a32aba97b8dba05c_medium.jpg",
"avatarfull": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/c0/c09ca9b316ff7bf7dccba6f5a32aba97b8dba05c_full.jpg",
"profileurl": "http://steamcommunity.com/id/LegLabs/",
"last_login": "2016-11-11T13:13:18.651Z",
"loccountrycode": "AL"
}
}
Using an Online Tool, I created these classes:
<Serializable>
Public Class mmr_estimate
Public Property estimate As String
Public Property stdDev As String
Public Property n As String
End Class
<Serializable>
Public Class profile
Public Property account_id As String
Public Property personaname As String
Public Property name As String
Public Property cheese As String
Public Property steamid As String
Public Property avatar As String
Public Property avatarmedium As String
Public Property avatarfull As String
Public Property profileurl As String
Public Property last_login As String
Public Property loccountrycode As String
End Class
<Serializable>
Public Class RootObject
Public Property tracked_until As String
Public Property solo_competitive_rank As String
Public Property competitive_rank As String
Public Property mmr_estimate As mmr_estimate
Public Property profile As profile
End Class
Then I use this code to deserialize it:
Dim steamData As String = ' the json contents above
Dim myjss As New JavaScriptSerializer()
Dim playerDictionary = myjss.Deserialize(Of List(Of RootObject))(steamData)
But the result I get is nothing, playerDictionary has 0 items, when it should have 1 item with the contents of the json parsed into KeyValuePairs.
If I use this piece of code
Dim data = myjss.DeserializeObject(steamData)
and then run a for each loop on the data elements, I can see the contents of data when debugging, but I don't know how to work with them like that, since they are just objects which I'm having trouble converting into KeyValuePairs, who in themselves may contain arrays of KeyValuePairs.
What I'm trying to get is the values of solo_competitive_rank, competitive_rank and steamid, but if I can't get the whole contents deserialized, I can't do that.
Are the declared classes wrong?
but with this solution you use not the class RootObject.
With first method, your JSON want in the list with key and value.
This is only for JSON Array. :(
rg

Gson.toJson not converting boolean values from object

Google's Gson.toJson(Object src) not converting boolean values.
For example, My Java object is say:
class MyObj {
private String name;
private boolean teen;
//getter,setter ommitted...
}
MyObj obj = new MyObj();
obj.setName("Me");
obj.isTeen(false);
Gson.toJson(obj);
While converting this object it's writing like this
MyObj { "name" : "Me" }
boolean is missing even though there was a value.
Gson doesn't serialize the fields only if the field is defined as transient or static.
If you haven't defined the boolean field as static or transient, json should have that field.
I am not sure how you are setting the value to boolean field using isTeen() method. It should give compilation error unless you have non-trivial "is" method for boolean field. The "is" method is basically an equivalent of "get" method for String/other fields. Typically, it won't be used to set the value.
I have just provided the full code with getters and setters. Please check whether this works.
public class MyObj {
private String name;
private boolean teen;
public String getName() {
return name;
}
public boolean isTeen() {
return teen;
}
public void setName(String name) {
this.name = name;
}
public void setTeen(boolean teen) {
this.teen = teen;
}
}
Main Method:-
public static void main(String[] args) {
Gson gson = new Gson();
MyObj obj = new MyObj();
obj.setName("Me");
obj.setTeen(false);
System.out.println(gson.toJson(obj));
}
Output:-
{"name":"Me","teen":false}
The interesting point is that even if you don't set any value for boolean field. It will take the default as false and the generated JSON will have false.

Silverlight 4 DataContractJsonSerializer, private fields of a derived class

I use DataContractJsonSerializer to deserialize json data in Silverlight 4.
Json data key names do not match my class property names; so I guess I have to use
DataMemberAttribute. So I did the following:
[DataContract]
public class Person : Model
{
[DataMember(Name = "id")]
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
[DataMember(Name = "name")]
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Now deserialization fails because I didn't apply DataContractAttribute to Person's base class Model. Is it a strict requirement? Also, after I applied DataContractAttribute to Model, deserialization fails again, because I applied DataMember attributes to private fields, not to the public properties. Why can't I apply them to private members (the documentation seems to say otherwise).
NOTE: server-side code is not ASP.NET; so WCF isn't used.
In order to get the private members to serialize over WCF correctly, we had to change them all to protected internal instead of private. Maybe the same applies for DataContractJsonSerializer?