i have the following method
public static <E> APIGatewayProxyResponseEvent generateResponse(E request, E response, int statusCode){
JSONObject result = new JSONObject();
result.put(Constants.REQUEST, request);
result.put(Constants.RESPONSE, response);
return new APIGatewayProxyResponseEvent()
.withBody(result.toString())
.withStatusCode(statusCode)
.withHeaders(Constants.commonHeaders);
}
i am getting net.sf.json.JSONException: java.lang.reflect.InvocationTargetException when result.put(Constants.RESPONSE, response); is executed
response is
Also the corresponding class is:
public class PhysicalMediaURL extends MediaURL {
private static final String IDENTIFIER_PREFIX = "images/I/";
public PhysicalMediaURL(String physicalId, String extension, MediaHostnameProvider mediaHostnameProvider) {
super("images/I/" + physicalId, extension, mediaHostnameProvider);
}
}
public abstract class MediaURL implements URL {
private final String identifier;
private final String extension;
private final MediaHostnameProvider mediaHostnameProvider;
public MediaURL(String identifier, String extension, MediaHostnameProvider mediaHostnameProvider) {
this.identifier = identifier;
this.extension = extension;
this.mediaHostnameProvider = mediaHostnameProvider;
}
public String getIdentifier() {
return this.identifier;
}
public String getExtension() {
return this.extension;
}
public String getDomainName() {
return this.mediaHostnameProvider.getMediaHostname(this.getExtension());
}
public String getURL() {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append("https://");
urlBuilder.append(this.getDomainName());
urlBuilder.append('/');
urlBuilder.append(this.getIdentifier());
urlBuilder.append('.');
urlBuilder.append(this.getExtension());
return urlBuilder.toString();
}
public List<String> getStyleTags() {
return null;
}
}
where PhysicalMediaURL is of type: URL and that is an interface
public interface URL {
String getIdentifier();
String getDomainName();
String getExtension();
List<String> getStyleTags();
String getURL();
}
I am a bit stuck in this.. need help.
First off it looks like you are using a JSON implementation that is not updated as regularly as the other ones(Your exception is from net.sf.json). I always recommend using the org.json implementation as it receives regular updates and bugfixes.
Most implementations of JSONObject, when used in this form, use bean based reflection to retrieve values from your object. This is not always what you want when your object is in an inheritance hierarchy because, depending on the object and the JSONObject impl, it will pull fields from the implementation that are not on your higher level type(URL in this case).
If you really want a generic serialization function use something like Jackson or Gson that will allow you to specify the type as a part of the serialization. Otherwise consider transforming your objects, before they are passed to your generateResponse function, into simpler objects such as a Map<String, String> that can serialize unambiguously.
As a final thought JSONObject's generic serialization works, but, its performance is likely to be worse than using a dedicated higher level serializer like Jackson. It's best used with the explicit put methods to generate simple objects.
Related
I have some nested classes in Java, simplified here. Getters and setters exist.
Example
public class Planet {
#JsonProperty("name")
private String name;
#JsonProperty("moons")
private List<Moon> moons;
}
public class Moon {
#JsonProperty("moonname")
private String name;
#JsonProperty("craters")
private int craters;
}
I want to be able to deserialize the records on mongo (following this same structure) to java objects on the rest controller, specifically the HTTP GET request.
#RestController
#RequestMapping("/planets")
public class PlanetController {
#Autowired
private PlanetService planetService;
#RequestMapping("/")
public List<Planet> getAllPlanets() {
//Need to deserialize here
return planetService.getAll();
}
#RequestMapping("/{name}")
public Planet getItemsWithName(#PathVariable("name") String name) {
//deserialize here
return planetService.getEntryWithName(name.toLowerCase());
}
PlanetService.getAll() is expecting return type of List. getEntryWithName() is expecting return type of Planet.
How can I loop the results in the getAll() so I can deserialize them before they are returned?
Using Jackson's object mapper, I can do the serialization of a Java object to a JSON object.
ObjectMapper mapper = new ObjectMapper();
try {
mapper.writeValue(new File("target/mars.json"), mars);
} catch (IOException e) {
e.printStackTrace();
}
I can probably use readValue for the opposite process but I don't know how to loop the results.
I will appreciate the help. Let me know if something is not clear.
public List<Planet> getAllPlanets() {
List<Planet> planets = planetService.getAll();
String jsonString = new ObjectMapper().writeValueAsString(planets);
return planets;
}
I have many servlets like this:
public class DoSmthServlet extends AbstractDTOServlet<DoSmthServlet.Params> {
#Override
public Response service(Params params) throws Exception {
// Some logic...
Response response = new Response();
response.field1 = data1;
response.field2 = data2;
// ...
return response;
}
public static class Params implements DomainDTO {
public long arg1;
public boolean arg2;
// ...
}
public static class Response implements DomainDTO {
public String field1;
public long field2;
// ...
}
}
I need to fill Response object with data, but it can contain really many fields. How to do it without writing many response.fieldN = dataN in each servlet? I don't want to write constructors for each class because I'll still have to write such assignments in constructors.
Maybe there is any library that can do it, or any pattern that I can use, or any way to generate constructors for Response classes?
Maybe Dozer bean mapper can help you. Some example:
Mapper mapper = new DozerBeanMapper();
DestinationObject destObject =
mapper.map(sourceObject, DestinationObject.class);
or
DestinationObject destObject = new DestinationObject();
mapper.map(sourceObject, destObject);
I don't know any library that can do it for you. But... you can use fluent builder which will simplify Response creation and make it a little bit... easier I think.
ex.
public class ResponseBuilder {
private Response response = new Response();
public ResponseBuilder withField1(String field1) {
response.field1 = field1;
return this;
}
public ResponseBuilder withField2(String field2) {
response.field2 = field2;
return this;
}
public Response build() {
return response;
}
}
// usage
Response response = Response.builder.withField1("a").withField2("b").build();
BTW: I would rather avoid writing constructor with many arguments for a Value Object/DTO class, because every argument passed to constructor should be considered as required.
If number of fields are same for all the Response object then write one static function and call it from different servlets. static function will do all response.fieldN = dataN
I'm trying to do something like this :
public class ResponseProcessorFactory {
public static <T> ResponseProcessor<T> newResponseProcessor(){
return new GsonResponseProcessor<T>();
}
}
public class GsonResponseProcessor<T> implements ResponseProcessor<T> {
protected T response;
protected TypeToken typeToken;
public GsonResponseProcessor() {
this.typeToken = new TypeToken<T>(){};
}
#Override
public void parse(String jsonString) throws JSONException, IOException {
response = GsonHelper.getGsonInstance().fromJson(jsonString, typeToken.getType());
}
public T getResponse() {
return response;
}
}
private void ResponseProcessor getResponseProcessor(){
return ResponseProcessorFactory<List<String>>.newResponseProcessor();
}
Now, whenever I invoke getResponseProcessor(), it doesn't return me the response processor for List<String>. Rather, it returns the default response processor for Object.
I'm sure, I'm missing some concept regarding generic. Can someone explain in detail ?
EDIT :
The real usage is like this :
public BaseRequestWithResponseProcessor<List<Dashboard>> getDashboards(Listener<List<Dashboard>> responseListener, ErrorListener errorListener) {
String url = mBaseUrl + "/dashboard";
ResponseProcessor<List<Dashboard>> responseProcessor = ResponseProcessorFactory.newResponseProcessor();
AuthInfo authInfo = getAuthInfo();
BaseRequestWithResponseProcessor<List<Dashboard>> request = new BaseRequestWithResponseProcessor<List<Dashboard>>(
Method.GET, url, authInfo, null, responseProcessor, responseListener, errorListener);
return request;
}
In the GsonResponseProcessor constructor type erasure has happened and at runtime only one version of the method will exist with the type variable T converted to Object.
In Java only one version of generic methods and classes will exist, the type parameters only exist during compile-time and will be replaced by Object during run-time.
Type tokens must be constructed with a concrete type to capture the type information. This is the whole point with them, to capture type information at a place where the concrete type is known. The token can then be stored in variables and later be used to lookup objects or get hold of the type information with reflection.
The solution here is that the caller of getResponseProcessor who knows the concrete type creates the type token and passes it as a parameter. You could also pass in a Class object if that works in you situation. If you want to use generic classes as tokens however, as in your example with List<Dashboard> you will need a type token.
Something like this:
ResponseProcessor<List<String>> p = ResponseProcessorFactory.newResponseProcessor(new TypeToken<List<String>>() {});
You can work around the type erasure by passing in the class type as method parameter.
public class ResponseProcessorFactory {
public static <T> ResponseProcessor<T> newResponseProcessor(Class<T> type){
return new GsonResponseProcessor<T>(type);
}
}
public class GsonResponseProcessor<T> implements ResponseProcessor<T> {
protected T response;
protected TypeToken typeToken;
public GsonResponseProcessor(Class<T> type) {
this.typeToken = TypeToken.get(type);//depends on the API version
//this.typeToken = new TypeToken<T>(type);
//this.typeToken = TypeToken.of(type);
}
#Override
public void parse(String jsonString) throws JSONException, IOException {
response = GsonHelper.getGsonInstance().fromJson(jsonString, typeToken.getType());
}
public T getResponse() {
return response;
}
}
Have you tried changing the signature to the correct type, too?
private ResponseProcessor<List<String>> getResponseProcessor() {
return ResponseProcessorFactory.newResponseProcessor();
}
I'm having problem to assign json data into java class.Please do help anyone,
My java class is like,
public class ListofGridRecords<T> {
public int Totalrecords;
public List<T> GridRecords;//using TraderTransaction class.
}
and TraderTransaction class is,
public class TraderTransaction {
public Date AddedTime;
public String TransactId;
public TransactStatus Status;
public String OtherPartyAccountNo;
public Double AmountPaid;
public Double AmountRecieved;
public Double ClosingBalance;
public TransactionTypes TransType;
public String Narration;
public TraderTransaction() {
super();
}
}
and my json conversion function look like,
JsonObject returndata = JsonObject.parse(responseString);
String operationresult = returndata.get("OperationResult").toString();
if (Result.values()[Integer.parseInt(operationresult)] == Result.Success) {
Gson gson = new Gson();
#SuppressWarnings("unchecked")
ListofGridRecords<TraderTransaction> traderlist =
gson.fromJson(returndata.get("ResultData").toString(), ListofGridRecords.class);
Log.i("LIST DATA:", "" + traderlist);
for (TraderTransaction trader: traderlist.GridRecords) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(TRANS_FIRST_COLUMN, currentformatter.format(trader.AddedTime));
map.put(TRANS_SECOND_COLUMN, trader.TransactId);
map.put(TRANS_THIRD_COLUMN, trader.OtherPartyAccountNo);
map.put(TRANS_FOURTH_COLUMN, trader.AmountPaid.toString());
map.put(TRANS_FIFTH_COLUMN, trader.AmountRecieved.toString());
map.put(TRANS_SIXTH_COLUMN, OpenOrClosed.values()[Integer.parseInt(trader.TransType.toString())].toString());
list.add(map);
}
}
I'm getting conversion error at for (TraderTransaction trader : traderlist.GridRecords).
My Json data look like,
{
"Messages":"RESULTS_RETRIEVAL_SUCCESSFULL",
"OperationResult":0,
"ResultData":{
"GridRecords":[
{
"AddedBy":"Distributor-9787457361-Rathinavel",
"AddedTime":"2013-04-12T16:26:24.0140117",
"AmountPaid":0.0,
"AmountRecieved":10000.0,
"ClosingBalance":10000.0,
"Narration":null,
"OtherPartyAccountNo":"0102849015327675",
"Status":2,
"TransType":2,
"TransactId":"TDRF483679051236"
},
{
"AddedBy":"Distributor-9787457361-Rathinavel",
"AddedTime":"2013-04-12T16:20:54.8681857",
"AmountPaid":0.0,
"AmountRecieved":0.0,
"ClosingBalance":0.0,
"Narration":null,
"OtherPartyAccountNo":"0102849015327675",
"Status":0,
"TransType":2,
"TransactId":"TDRF706925413802"
}
],
"Totalrecords":2
},
"UpdateAvailable":"0"
}
In order to parse your JSON, I'd use a slightly different strategy. As you seem to be interested in parsing only the "ResultData", I'd create classes to wrap the response, very similar to those you have already created, namely:
public class Response {
#SerializedName("ResultData")
public ResultData resultData;
}
and,
public class ResultData {
#SerializedName("GridRecords")
public List<GridRecord> gridRecords;
#SerializedName("Totalrecords")
public int totalrecords;
}
and,
public class GridRecord {
#SerializedName("AddedTime")
public String addedTime;
#SerializedName("TransactId")
public String transactId;
//other fields...
}
and other classes if necessary...
Then, in order to parse your JSON reponse, you just have to do:
Gson gson = new Gson();
Response data = gson.fromJson(responseString, Response.class);
and you'll be able to access any field, for example:
data.resultData.gridRecords.transactId;
Note 1: If you are interested in more fields of the JSON response, you just have to add more fields to your wrap classes, according to the JSON response...
Note 2: I've changed the type of addedTime to String, instead of Date because it throws an exception for unparseable date. Anyway I usually leave the types in the Response objects as simple String and then in the class from where I retrieve the response, I do the correct formatting while creating my objects, for example, when you put the values in your Map...
Note 3: The use of the annotation #SerializedName is interesting to separate the name of a field in the JSON response and in your app, in order to follow Java naming conventions, which your attributes are not following...
Note 4: You shouldn't use public attributes in your classes. It's more recommendable to use private/protected attributes and their correspondent getters and setters...
I have the following Enum:
public enum MyState {
Open("opened"),
Close("closed"),
Indeterminate("unknown");
private String desc;
private MyState(String desc) {
setDesc(desc);
}
public String getDesc() {
return this.desc;
}
private void setDesc(String desc) {
this.desc = desc;
}
}
I am trying to write an XStream Converter that will know to map back a JSON element "mystate" to a MyState instance.
"someJson": {
"object1": {
"mystate": closed
}
}
This should produce, amongst other objects (someJson and object1) a MyState.Close instance. I've started the Converter, but haven't gotten very far:
public class MyStateEnumConverter implement Converter {
#Override
public boolean canConvert(Class clazz) {
return clazz.equals(MyState.class);
}
#Override
public void marshal(Object value, HierarchialStreamWriter writer, MarshallingContext context) {
??? - no clue here
}
#Override
public Object unmarshal(HierarchialStreamReader reader, UnmarshallingContext context) {
??? - no clue here
}
}
Then, to create the mapper and use it:
XStream mapper = new XStream(new JettisonMappedXmlDriver());
mapper.registerConverter(new MyStateEnumConverter);
SomeJson jsonObj = mapper.fromXML(jsonString);
// Should print "closed"
System.out.println(jsonObject.getObject1().getMyState().getDesc());
How can I implement marshal and unmarshal so thatI get the desired mapping? Thanks in advance!
You can accomplish this by doing 2 things:
Adding a lookup method as well as a toString() override to your enum (MyStateEnum); and
Extending XStream's AbstractSingleValueConverter instead of implementing Converter
MyStateEnum:
public enum MyStateEnum {
// Everything you had is fine
// But now, add:
public static MyStateEnum getMyStateByDesc(String desc) {
for(MyStateEnum myState : MyStateEnum.values())
if(myState.getDesc().equals(desc))
return myState;
return null;
}
#Override
public String toString() {
return getDesc();
}
}
MyStateEnumConverter:
public class MyStateEnumConverter extends AbstractSingleValueConverter {
#Override
public boolean canConvert(Class clazz) {
return clazz.equals(MyStateEnum.class);
}
#Override
public Object fromString(String parsedText) {
return MyStateEnum.getMyStateByDesc(parsedText);
}
}
By adding getMyStateByDesc(String) to your enum, you now have a way to look up all the various enumerated values from the outside, by providing a desc string. The MyStateEnumConverter (which extends AbstractSingleValueConverter) uses your toString() override under the hood to associate aMyStateEnum instance with a text string.
So when XStream is parsing the JSON, it sees a JSON object of, say, "opened", and this new converter knows to pass "opened" into the converter's fromString(String) method, which in turn uses getMyStateByDesc(String) to lookup the appropriate enum instance.
Don't forget to register your converter with your XStream instance as you already showed in your original question.
You can use the EnumToStringConverter
Documentation
Example
#XStreamConverter(EnumToStringConverter.class)
public enum MyStateEnum {
enter code here
...
Use xstream.autodetectAnnotations(true)
Why are you using xstream for json support? You have a couple of other libraries specialized in json and that do it well. Also closed without quotes is not valid json.
Try for example Genson, it will work out of the box.
The values in the json stream would be "Close", "Indeterminate", etc and when deserializing it will produce the correct enum.
class SomeObject {
private MyState state;
...
}
Genson genson = new Genson();
// json = {"state" : "Indeterminate"}
String json = genson.serialize(new SomeObject(MyState.Indeterminate));
// deserialize back
SomeObject someObject = genson.deserialize(json, SomeObject.class);
// will print unknown
System.out.println(someObject.getDesc());