JSON number to String - java

One of my JSON content has number for entityTypeId, how to change this to a String ?
eg change 1 to "1".
JSON
[
{
entityTypeId: 3,
entityTypeName: "Branch of Legal Entity"
},
{
entityTypeId: 1,
entityTypeName: "Legal Entity"
},
{
entityTypeId: 2,
entityTypeName: "Notional Entity"
}
]
REST API
#GET
#Path(value = "/entityTypes")
#Produces(MediaType.APPLICATION_JSON)
#Override
public List<EntityType> getEntityTypes() {
return commonBusiness.getEntityTypes();
}
JPA Entity
public class EntityType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="ENTITY_TYPE_ID")
private long entityTypeId;
#Column(name="ENTITY_TYPE_NAME")
private String entityTypeName;
Update:
Many of you asked why I need to change to a String. I use this JSON data to render a drop-down. This drop down value (entityTypeId) saves in the DB in a number column successfully. But when I load the view page the drop-down is not loaded with that value. Other drop downs work which has both those values as String.
Earlier I raised a separated issue
Angularjs - dropdown - Should the key/value pair always be a string data type

In your EntityType class you would need to change the type of the entityTypeId to be a String, but there might be an impact if you do that, so you need to think about what that column accepts in the database. The bigger question is why do you want to change your data type to be a String.

I think you should make 'entityTypeId' as String instead of long.

I suggest to use a DTO which contains the data as String, and do the conversion in the service layer.

I think you should try this. json = json.replace (/:(\d+)([,}])/g, ':"$1"$2');
If you have used json library.

If I understand correctly, the requirement here is to have the REST service json response to have entityType as a number.
This can be achieved by creating a json response using a custom serializer.
#GET
#Path(value = "/entityTypes")
#Produces(MediaType.APPLICATION_JSON)
#Override
public Response getEntityTypes() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("entityModule",
Version.unknownVersion());
module.addSerializer(EntityCollection.class, new EntityTypeJsonSerializer());
mapper.registerModule(module);
String responseJson = mapper.writeValueAsString(commonBusiness.getEntityTypes());
return Response
.status(Status.OK)
.entity(responseJson)
.type(MediaType.APPLICATION_JSON).build();
}
Create an interim collection class
public class EntityCollection{
private List<EntityType> entityTypes;
}
Custom Serializer:
public class EntityTypeJsonSerializer extends JsonSerializer<EntityCollection> {
#Override
public void serialize(EntityCollection entityTypes, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
// JSON parsing goes here
jgen.writeString(String.valueOf(entityType.get(entityTypeId)));
}
}
This will make your JPA entity and response JSON independent.

I don't think using number is an issue in a select.
Check out this example I've created.
<div ng-app="myApp" ng-controller="myController">
<select ng-model="entity.selected">
<option value="{{ent.entityTypeId}}" ng-repeat="ent in entityTypes">{{ent.entityTypeName}}</option>
</select>
Selected entity id {{entity.selected}}
</div>
JSFiddle
I've also updated your other question. Let me know if this is what you were looking for.

Related

Retrofit2 handle JSON response that contains two models data

I'm using retrofit2 to handle http request after calling from API. Let me explain this.
I have 2 java class(POJO) created to handle user and lecturer data which is User.java and Lecturer.java respectively. For the response data such as :
{
"users": [
{
"user_id": "28",
"user_email": "john#abc.com",
"user_password": "123"
}
]
}
i can use User.java class to handle this response. Nothing complex in this file, only contains getter and setter method. Same goes to lecturer data, here is the example of lecturer data :
{
"lecturers": [
{
"lecturer_id": "3",
"user_id": "28",
"lecturer_name": "johny2"
}
]
}
i can handle it by using Lecturer.java class.
But the problem is, if the response contains both user and lecturer data on a single json, how to handle it?? . Here is the example of request :
{
"users": [
{
"user_id": "28",
"user_email": "john#abc.com",
"user_password": "123",
"lecturer_id": "3",
"lecturer_name": "johny2"
}
]
}
To solve this problem, i think i need to create another java class that contains both User and Lecturer class on it, unfortunately at here i'm stuck.
This is new file, that i tried to create (Userlecturer.java) :
public class UserLecturer {
User user;
Lecturer lecturer;
// how to implement on this part
}
Here is UserLecturer interface :
public interface UserLecturerInterface {
#GET ( "api/endpoint/here" )
Call<UserLecturer> getLecturerByUserId (#Path( "userId" ) String userId );
}
Appreciated for any helps. Ask me for more inputs if above use case did't clear enough. Thanks
I think the POJO should be:
public class Users {
String userId;
String userEmail;
String userPassword;
String lecturerId;
String lecturerName;
}
Even though there are 2 models inside the JSON, you only need 1 model for Retrofit.
If you really want to split the 1 JSON response into 2 models, I think you have to implement custom JSON converter.
Gson gson = new GsonBuilder()
.registerTypeAdapter(UserLecture.class, new JsonDeserializer<UserLecture>() {
public UserLecture deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonArray usersJsonArray = json.getAsJsonObject().getAsJsonArray("users");
JsonObject userJsonObject = usersJsonArray.getAsJsonArray().get(0).getAsJsonObject();
User user = new User();
user.setUserId(userJsonObject.get("user_id").getAsString());
user.setUserEmail(userJsonObject.get("user_email").getAsString());
user.setUserPassword(userJsonObject.get("user_password").getAsString());
Lecturer lecturer = new Lecturer();
lecturer.setLecturerId(userJsonObject.get("lecturer_id").getAsString());
lecturer.setLecturerName(userJsonObject.get("lecturer_name").getAsString());
return new UserLecture(lecturer, user);
}
})
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl([YOUR_BASE_URL])
.addConverterFactory(GsonFactoryConverter.create(gson))
.build();
This is some code I use to convert longs to Java Date objects.
Presumably, you can do the same thing for your UserLecture object. You should be able to extract the individual json objects for User and Lecture, create a new UserLecture object and let User and Lecture as objects in it.
Gson gson = new GsonBuilder().registerTypeAdapter(UserLecture.class, new JsonDeserializer<UserLecture>() {
public UserLecture deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject user = json.getAsJsonObject().getAsJsonObject("user");
JsonObject lecture = json.getAsJsonObject().getAsJsonObject("lecture");
return new UserLecture(user, lecture);
}
}).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonFactoryConverter.create(gson))
.build();
Then inside UserLecture:
public UserLecture(JsonObject userJson, JsonObject lectureJson) {
this.user = new User();
this.user.setUserId(userJson.get("user_id").getAsInt());
this.user.serUserEmail(userJson.get("user_email").getAsString());
//so on.
}
At first let me say that the JSON you need to process here is broken by design so you should urge the guy / department / company to fix it.
Secondly, JSON processors like Jackson allow to parse polymorphic data structures like this easily, but they require some kind of type flag to distinguish one of another type (i.e. type: "user" and type: "lecturer"). There is also a way to do this without such type flags, but there is a lot more hand work involved. The last example here shows how to do it.
Yes, it is one possible solution. Gson ignores all fields, which names doesnt match #SerializedName annotation. So, you may try another solution without creating any more pojo classes- return result as String, and try to parse this string as both classes. If one result is empty- then you have another. But, if both kbjects isnt empty- then original response contain fields from both pojos

Parse JSON without object name in Java

I am trying to parse this JSON which is coming as the response to a REST API call. Can you please help me parsing it as key value pairs?
The object names are not present. There is nesting as well. There seems to be no new line between records.
The aim is to extract this data and load it into a database.
[
{
"cc_emails":["feedback#xyz.com"],
"fwd_emails":[],
"reply_cc_emails":["feedback#xyz.com"],
"fr_escalated":false,
"spam":false,
"email_config_id":6000038087,
"group_id":6000110481,
"priority":1,
"requester_id":6010410791,
"responder_id":6002817857,
"source":1,
"company_id":null,
"status":2,
"subject":"fare",
"to_emails":["feedback#xyz.com"],
"product_id":null,
"id":45043,
"type":null,
"due_by":"2016-03-12T08:58:02Z",
"fr_due_by":"2016-03-08T08:58:02Z",
"is_escalated":false,
"description":"Dear xyze Team,\r\n\r\nWhy r u increased fair again and againasas0mail.gmail.com</a>.<br>\n",
"custom_fields":
{
"category":null,
"issue":null,
"route_id":null,
"phone_number":null,
"department":null,
"booking_id":null
},
"created_at":"2016-03-07T08:58:02Z",
"updated_at":"2016-03-07T08:58:03Z",
// ...... repeat
}
]
The best way to do this would be to use http://www.jsonschema2pojo.org/
Enter your json there
Change source type to JSON
set the correct class name and package.
The resulting pojo can be directly mapped from the json
If you are using resttemplate to hit the api then you can use getForObject to automatically set the pojo from the output.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject-java.lang.String-java.lang.Class-java.lang.Object...-
Using gson you can do this quite simply.
Do a class to match the fields in the json something like:
public class Example {
private List<String> cc_emails;
private List<String> fwd_emails;
private List<String> reply_cc_emails;
private Boolean fr_escalated;
private Boolean spam;
private Integer email_config_id;
...
private CustomFields custom_fields;
private String created_at;
private String updated_at;
}
Then you need to do another to map the custom fields
public class CustomFields {
private String category;
...
}
And using json you can parse it like this:
Type type = new TypeToken<Collection<Example>>(){}.getType();
new Gson().fromJson(json,type);
You have to exaplain to Gson it's a list, if it was a single object it would be this:
new Gson().fromJson(json,Example.class);
This is the aproach I usually take, also in the dates java.sql.Timestamp class might also parse it, you would need to try it though.
You can use Gson (https://github.com/google/gson) or Jackson (https://github.com/FasterXML/jackson) and deserialize it to a Map.

cloud endpoint not respecting Jackson annotations

I have a class Organization with few date fields as follows.
public class Organization {
private String _id;
private String name;
#JsonDeserialize(using=JsonDateDeserializer.class)
#JsonSerialize(using=JsonDateSerializer.class)
private Date createdTime;
// getters and setters
}
To handle the date in simple way on client side I convert date to long and send it to client using these JsonSerializer as follows
public class JsonDateSerializer extends JsonSerializer<Date>{
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
gen.writeNumber(date.getTime());
}
}
I have a endpoint which will execute get organization and insert the results in response map like this.
#ApiMethod(name="organization.get", path="organizations/{organizationId}", httpMethod=HttpMethod.GET)
public Map<String, Object> organizationDetails(#Named("organizationId") String organizationId){
Organization org = DB.getOrganization("123");
Map<String, Object> response = new HashMap<String, Object>();
response.put("status", HttpServletResponse.SC_OK);
response.put("success", true);
response.put("entity", org);
return response;
}
But the end resultant JSON I see on client side is
{
"status" : 200,
"entity" : [ {
"_id" : "966a03b3-8e46-41ee-b330-6533409b2b4a",
"name" : "POKURI",
"createdTime" : "2015-05-16T15:02:31.499+05:30"
} ],
"success" : true
}
Here the date coming in some format instead of long. If I convert the same using Jackson ObjectMapper without using cloud endpoints I am getting response in expected way. Why cloud endpoints not respecting Jackson annotations? Is there a way to configure that?
Note: Even I observed that long is coming as string on client side if you use cloud endpoints. I am using appengine SDK 1.9.19
You aren't supposed to use Jackson annotations with Endpoints (and if you do, you have to use the repackaged version, not your own). You can do what you want with #ApiResourceProperty or #ApiTransformer, depending on how you want to do it. See the annotation documentation here.
Regarding long, it is serialized as a string due to floating point imprecision--it's not possible to represent all values of a long accurately using a double, which is what JavaScript and JSON.parse will store a numeric in, so it is always transmitted as a string. Any of our client libraries for Endpoints automatically convert them to the correct data type.

Parse JSON array with jackson where objects are strings?

I have a slightly odd question. I have created an object, let's call it a Profile, that successfully parses single JSON objects via an API that I call. There is also a multi-profile interface that will return a JSON array of Profile objects. The problem is, the multi-profile interface turns the sub objects into strings. Is there an automatic way I can tell jackson to parse these into objects?
Example of a single object:
{ "foo": "bar" }
Example of a multi object:
[ "{ \"foo\": \"bar\" }", "{ \"blah\": \"ugh\" }" ]
(Sorry can't use real data)
Notice that the sub objects are actually strings, with escaped quotes inside them.
For completeness, my code for the multi object parse looks like this:
ObjectMapper mapper = new ObjectMapper();
Profile[] profile_array = mapper.readValue(response.content, Profile[].class);
for (Profile p: profile_array)
{
String user = p.did;
profiles.put(user, p);
}
As I said, in the single-profile case, the Profile object parses. In the multi-profile case, I get this exception:
Exception: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.xyz.id.profile.Profile, problem: no suitable creator method found to deserialize from JSON String
I suppose you'll have to create a custom deserializer and apply it to the every element of that array.
class MyCustomDeserializer extends JsonDeserializer<Profile> {
private static ObjectMapper om = new ObjectMapper();
#Override
public Profile deserialize(JsonParser jp, DeserializationContext ctxt) {
// this method is responsible for changing a single text node:
// "{ \"foo\": \"bar\" }"
// Into a Profile object
return om.readValue(jp.getText(), Profile.class);
}
}
There is no out-of-the-box support for "re-parsing" of embedded JSON-in-JSON content.
But this sounds like a possible request for enhancement (RFE)...
Have you tried using JAXB?
final ObjectMapper mapper = new ObjectMapper();
// Setting up support of JAXB
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
// make deserializer use JAXB annotations (only)
mapper.getDeserializationConfig().setAnnotationIntrospector(
introspector);
// make serializer use JAXB annotations (only)
mapper.getSerializationConfig().setAnnotationIntrospector(
introspector);
final StringReader stringReader = new StringReader(response);
respGetClasses = mapper.readValue(stringReader,
FooBarClass.class);
The above should get you started...
Also, you would need to mark each subclass like so:
#XmlElement(name = "event")
public List<Event> getEvents()
{
return this.events;
}

Play Framework: Rendering custom JSON objects

I am using Play Framework 1.2.4 with Java and using JPA to persist my database objects. I have several Model classes to be rendered as JSON. But the problem is I would like to customize these JSON responses and simplify the objects just before rendering as JSON.
For instance, assume that I have an object named ComplexClass and having properties id, name, property1,...,propertyN. In JSON response I would like to render only id and name fields.
What is the most elegant way of doing this? Writing custom binder objects or is there simple JSON mapping such as using a template?
Play Framework 1.2.4 directly depends on the gson library so you could use that to render your JSON strings. All you have to do is use gson's #Expose annotation. So in your example, you would mark the fields you want in your JSON string like this:
public class ComplexClass {
#Expose
public Long id;
#Expose
public String name;
...
}
Then in your controller, you would just do this:
public static void someActionMethod() {
// get an instance of your ComplexClass here
ComplexClass complex = ...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
String json = gson.toJson(complex);
renderJson(json);
}
See documentation here.
If ComplexClass is actually a play.db.jpa.Model and therefore the id field is abstracted away in a parent class and you can't put the #Expose annotation on it, then you could create your own ExclusionStrategy that skips fields that aren't annotated with #Expose and are not called id. So something like this (pseudo-code):
public final class ComplexClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes attributes) {
if (name of field is "id") return false;
if (field is annotated with #Expose) return false;
return true;
}
Then the controller would altered slightly to look like this:
GsonBuilder builder = new GsonBuilder();
ComplexClassExclusionStrategy strategy = new ComplexClassExclusionStrategy();
builder.setExclusionStrategies(strategy);
Gson gson = builder.create();
String json = gson.toJson(complex);
renderJson(json);
Use FlexJSON, it's really easy. It allows you to create JSONSerializers which can include/exclude the fields you want.
Check out this article for some examples of using it with Play! Framework.
Here's a simple example:
public ComplexClass {
public Long id;
public String name;
// And lots of other fields you don't want
public String toJsonString() {
// Include id & name, exclude all others.
JSONSerializer ser = new JSONSerializer().include(
"id",
"name",
).exclude("*");
return ser.serialize(this);
}
}
You can add it to your dependencies.yml like so:
require:
- play
- net.sf.flexjson -> flexjson 2.1
What I usually do is write an interface for models that implements a toJSONString() method so that I can call renderJSON(someModel.toJSONString()) in the controller.
Link to official website
EDIT: Extra example for lists/collections
Ok, when you start serializing list you might get some unexpected results. This is because the order of evaluation is important. The first include() or exclude() takes precedence over the following ones.
Here's an example of serializing the childs of a parent entity (OneToMany relation).
JSONSerializer ser = new JSONSerializer();
// Exclude these standard fields from childs
ser.exclude(
"*.persistent",
"*.class",
"*.entityId"
);
// Include childs and all its other fields
ser.include(
"childs",
"childs.*"
);
// Exclude everything else
ser.exclude("*");
String data = ser.serialize(parent);
The * is a wildcard by the way. This piece of documentation explains it perfectly:
An exclude of *.class will match to any path depth. So if flexjson is serializing the field with path of "foo.bar.class" the * in *.class will match foo.bar.

Categories