I have been struggling with this, and I can't seem to get it right. I have a Java Spring Boot project and I need to create an API that returns the following JSON in what I called AggregatedResponse:
{
"shipments": {
"987654321": ["BOX", "BOX", "PALLET"]
},
"track": {
"123456789": "COLLECTING"
},
"pricing": {
"NL": 14.242090605778
"CN": 20.503467806384
}
}
Each of the objects (shipments, track, pricing) need to be fetched from an external API.
This means that I need to create the AggregatedResponse and use a setter whenever I'm receiving the different data from the external APIs.
The number in the shipments (987654321) comes from the request param passed to the external API that retrieves shipments. The track number (123456789) also comes from an external API.
This is the Java structure I have so far:
public class AggregatedResponse {
private TrackResponse tracking;
private ShipmentsResponse shipments;
private PricingResponse pricing;
public ShipmentsResponse getShipments() {
return shipments;
}
public void setShipmentsResponse(ShipmentsResponse value) {
this.shipments = value;
}
public TrackResponse getTrackResponse() {
return tracking;
}
public void setTrackResponse(TrackResponse trackResponse) {
this.tracking = trackResponse;
}
public PricingResponse getPricing() {
return pricing;
}
public void setPricing(PricingResponse value) {
this.pricing = value;
}
}
The problem I have is that like I mentioned, the shipments and track JSON object have dynamic names, so I have tried different strategies for creating the TrackResponse and the ShipmentsResponse.
How could I do this?
Create a Map from the data instead of class so you can define the keys and serialize it.
Related
I'm trying to read from a JSON response using a pojo. The response has many attributes but I only need few, I created the POJO with only those attributes, but when I'm trying to read it. it fails to parse it.
JSON response:
[
{
"attr_1":1,
"attr_2":2,
"attr_3":3,
"attr_4":4,
"attr_5":5,
},
{
"attr_1":10,
"attr_2":20,
"attr_3":30,
"attr_4":40,
"attr_5":50,
}
]
my POJO:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyPOJO {
#JsonProperty("attr_2")
private int attr_2;
#JsonProperty("attr_4")
private int attr_4;
public int getattr_2() { return attr_2; }
public void setattr_2(int attr_2) { this.attr_2 = attr_2;}
public int getattr_4() { return attr_4; }
public void setattr_4(int attr_4) { this.attr_4 = attr_4;}
}
I only need attr_2 and attr_4, and not the others.
this is how I'm trying to read the response:
MyPOJO[] arr = response.readEntity(MyPOJO[].class);
but it fails at this step. do we really need to have all the attributes in my POJO as there are many and I need few attributes, don't want to have redundant code?
I was able to solve the issue. I created an empty constructor and it worked. previously I had a constructor with the two needed attributes only.
I'm thinking at the moment about the solution of a problem which might be already solved by a pattern but I can not find the correct one. I'll try to explain what I would like to do with a simplefied example.
There is a class which handles to connection to a database, let's call it DatabaseManager.java. With this class I would like to handle the fetchment of data from a database and I also would like to apply filter.
public class DatabaseManager {
DatabaseFilter databaseFilter = new DatabaseFilter();
public DatabaseManager() {
// Do some stuff to init db connection
}
public DatabaseFilter configureFilter() {
return databaseFilter;
}
public String getStringDataset() {
String dataset = null;
// Fill dataset with applied filter data
return dataset;
}
}
With the method call configureFilter() on the databse object, I would like to get the DatabaseFilter which contains particular nested filter classes.
public class DatabaseFilter {
int[] filteredIds = null;
public IdFilter onId() {
return new IdFilter();
}
public class IdFilter {
private void exclude(int[] ids) {
filteredIds = ids;
}
}
}
On this way, I could write the follwing nice syntax to configure a filter which excludes particular filter.
DatabaseManager database = new DatabaseManager();
database.configureFilter().onId().exclude(idArray);
// Get filtered dataset
database.getStringDataset();
Is this an appropriate way to solve problems like this and is this a particular pattern? Are there any disadvantages?
Error is obviously after the remote procedure/method has been executed. It's most likely that the remote service is sending back the incorrect data or so I think. I have tried increasing the readQuote in Web.Config, that didn't help.
Response Class
When this property (MultiSMSPostedList) is set, the value is NULL. As soon as the code comes out of get, the exception is thrown.
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.5485")]
[System.SerializableAttribute()]
//[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.SoapTypeAttribute(Namespace="urn")]
public partial class MessagingServices_SendMultiSMSResult : object, System.ComponentModel.INotifyPropertyChanged {
private int sMSCountField;
private MessagingServices_SendMultiSMS_SendSuccess[] multiSMSPostedListField;
private MessagingServices_SendMultiSMS_SendFailed[] multiSMSRejectedListField;
/// <remarks/>
public int SMSCount {
get {
return this.sMSCountField;
}
set {
this.sMSCountField = value;
this.RaisePropertyChanged("SMSCount");
}
}
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(IsNullable=true)]
public MessagingServices_SendMultiSMS_SendSuccess[] MultiSMSPostedList {
get {
return this.multiSMSPostedListField;
}
set {
this.multiSMSPostedListField = value;
this.RaisePropertyChanged("MultiSMSPostedList");
}
}
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(IsNullable=true)]
public MessagingServices_SendMultiSMS_SendFailed[] MultiSMSRejectedList {
get {
return this.multiSMSRejectedListField;
}
set {
this.multiSMSRejectedListField = value;
this.RaisePropertyChanged("MultiSMSRejectedList");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
Exception
The exception text is below, it clear that exception is caused when system tries to convert variable to array.
http://justpaste.it/sms-exception
More information
Further digging shows that the remote service is returning the correct number of values, if not the format.
This property MultiSMSPostedList makes call to below class based on the number of values passed to the original procedure/method. (i.e. if I pass two phone numbers and two text messages, the properties inside MessagingServices_SendMultiSMS_SendSuccess are initialized twice, indicating remote server returned an array)
Property class
public partial class MessagingServices_SendMultiSMS_SendSuccess : object, System.ComponentModel.INotifyPropertyChanged {
private string gRecipientNameField;
private string gRecipientMSISDNwithCCField;
private int gOutMsgIDField;
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(IsNullable=true)]
public string gRecipientName {
get {
return this.gRecipientNameField;
}
set {
this.gRecipientNameField = value;
this.RaisePropertyChanged("gRecipientName");
}
}
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(IsNullable=true)]
public string gRecipientMSISDNwithCC {
get {
return this.gRecipientMSISDNwithCCField;
}
set {
this.gRecipientMSISDNwithCCField = value;
this.RaisePropertyChanged("gRecipientMSISDNwithCC");
}
}
/// <remarks/>
public int gOutMsgID {
get {
return this.gOutMsgIDField;
}
set {
this.gOutMsgIDField = value;
this.RaisePropertyChanged("gOutMsgID");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
I am sure, visual studio is not able to generate the class correctly based on the WSDL data, I may need to make some changes to reference.cs file. I am not an expert on the subject, so not really sure what and where should I make the change?
Could be useful
Remote service is java based, axis web service.
MORE INFO: Changes made to reference.cs
I made below changes to Response Class and it's working without any exception. Since it's a variable instead of array, I get only first response and all the other response values are lost.
private MessagingServices_SendMultiSMS_SendSuccess[] multiSMSPostedListField;
private MessagingServices_SendMultiSMS_SendFailed[] multiSMSRejectedListField;
I am not sure where exactly in the reference.cs I can find the location, where remote call is made/ends, may be making some changes there could help. Any suggestions?
I have to move from an old Java code to a new one using Play! Framework.
In the old code, I called a Java servlet using Ext-Js (Javascript Framework) using this way :
function getTree()
{
var store = Ext.create('Ext.data.TreeStore',
{
root:
{
text: 'System',
id: 'root',
expanded: true
},
proxy:
{
type: 'ajax',
url: 'TreeServlet',
extraParams:
{
mode:'getChildren'
},
reader:
{
type:'json',
root:'result'
}
}
});
Now, I would like to use Play! to do the same, but I do not know how to use it.
. In routes.conf:
GET /tree controllers.Application.makeTree()
. In controller.Application:
public static Result makeTree(){
// What shoul I put here to call the "Servlet"
}
I do not want to use Servlet, but I don't know how to do it.
Thank you for you help!
EDIT 1:
Thank you to all of you!
Here is how I eventually manage to achieve my goal:
public class Tree extends Controller
{
private MenuManager menuManager;
String node;
String mode;
String hash;
ObjectNode response;
public void createTree() throws IOException{
this.menuManager = MenuManager.getMenuManager();
getParams();
createJson(mode, node, hash);
}
public static Result returnJson() throws IOException{
Tree t = new Tree();
t.createTree();
return ok(t.response);
}
}
And in routes:
GET /tree controllers.Tree.returnJson()
What do you guys think? Good practice?
In earlier play frameworks you have to create only static methods for each and every request handler in controller.
But in the newer version (after play 2.0) you don't need to have static methods you can use normal public methods and configure it in routes prefixed with '#' symbol.
And don't maintain or declare attributes within controller class.
Because play is an event driven framework not like oridinary servlet based framework.
It provides REST and it doesn't maintain any httpsession like in servlets.
Session is available in the form of cookies only.
Below is the remodified version of your code,
public class TreeController extends Controller
{
public void createTree() throws IOException{
MenuManager menuManager = MenuManager.getMenuManager();
String mode = request().getQueryString("mode");
String node = request().getQueryString("node");
String hash = request().getQueryString("hash");
TreeNodeDto treeObject = menuManager.buildTree();
ok(treeObject.toJson());
}
}
public class BaseDto<T extends BaseDto<T>> implements Serializable{
public JsonNode toJson() {
return Json.toJson(this);
}
public T fromJson(JsonNode jsonObject) {
return (T) Json.fromJson(jsonObject, this.getClass());
}
}
public static class TreeNodeDto extends BaseDto {
public String hash;
public String name;
public Set<TreeNodeDto> children;
// Override equals and hashcode, because we are using "set" to maintain the child nodes.
}
routes
GET /tree #controllers.TreeController.createTree()
Hope this will give some ideas.
Cheers..!!!
Check the WS object: https://www.playframework.com/documentation/2.3.x/JavaWS
It seems that the return from the Servlet is a Json so check how to process json in play here: https://www.playframework.com/documentation/2.3.x/JavaJsonActions
I believe something like that should do it
public static Promise<Result> makeTree() {
final Promise<Result> resultPromise = WS.url("TreeServlet").setHeader("Content-Type", "application/json").get().map(
new Function<WSResponse, Result>() {
public Result apply(WSResponse response) {
return ok("Feed title:" + response.asJson().findPath("result"));
}
}
);
return resultPromise;
}
Below is the structure for your http request in play,
public static Result makeTree() {
TreeDto treeDto=new TreeDto();
JsonNode jsonResponse = Json.newObject();
try {
treeDto = <<Logic to get the tree objects from db>>;
if(treeDto != null) {
jsonResponse = Json.toJson(treeDto);
}
} catch (XODAOException e) {
Logger.error("Error while building the tree.", e);
jsonResponse = generateErrorResponse("Error while building tree.", e);
}
return ok(jsonResponse);
}
I am trying to incorporate a data cache for one of my GWT widgets.
I have a datasource interface/class which retrieves some data from my backend via RequestBuilder and JSON. Because I display the widget multiple times I only want to retrieve the data once.
So I tried to come with an app cache. The naive approach is to use a HashMap in a singleton object to store the data. However I also want to make use of HTML5's localStorage/sessionStorage if supported.
HTML5 localStorage only supports String values. So I have to convert my object into JSON and store as a string. However somehow I can't come up with a nice clean way of doing this. here is what I have so far.
I define a interface with two functions: fetchStatsList() fetches the list of stats that can be displayed in the widget and fetchStatsData() fetches the actual data.
public interface DataSource {
public void fetchStatsData(Stat stat,FetchStatsDataCallback callback);
public void fetchStatsList(FetchStatsListCallback callback);
}
The Stat class is a simple Javascript Overlay class (JavaScriptObject) with some getters (getName(), etc)
I have a normal non-cachable implementation RequestBuilderDataSource of my DataSource which looks like the following:
public class RequestBuilderDataSource implements DataSource {
#Override
public void fetchStatsList(final FetchStatsListCallback callback) {
// create RequestBuilderRequest, retrieve response and parse JSON
callback.onFetchStatsList(stats);
}
#Override
public void fetchStatsData(List<Stat> stats,final FetchStatsDataCallback callback) {
String url = getStatUrl(stats);
//create RequestBuilderRquest, retrieve response and parse JSON
callback.onFetchStats(dataTable); //dataTable is of type DataTable
}
}
I left out most of the code for the RequestBuilder as it is quite straightforward.
This works out of the box however the list of stats and also the data is retrieved everytime even tough the data is shared among each widget instance.
For supporting caching I add a Cache interface and two Cache implementations (one for HTML5 localStorage and one for HashMap):
public interface Cache {
void put(Object key, Object value);
Object get(Object key);
void remove(Object key);
void clear();
}
I add a new class RequestBuilderCacheDataSource which extends the RequestBuilderDataSource and takes a Cache instance in its constructor.
public class RequestBuilderCacheDataSource extends RequestBuilderDataSource {
private final Cache cache;
publlic RequestBuilderCacheDataSource(final Cache cache) {
this.cache = cache;
}
#Override
public void fetchStatsList(final FetchStatsListCallback callback) {
Object value = cache.get("list");
if (value != null) {
callback.fetchStatsList((List<Stat>)value);
}
else {
super.fetchStatsList(stats,new FetchStatsListCallback() {
#Override
public void onFetchStatsList(List<Stat>stats) {
cache.put("list",stats);
callback.onFetchStatsList(stats);
}
});
super.fetchStatsList(callback);
}
}
#Override
public void fetchStatsData(List<Stat> stats,final FetchStatsDataCallback callback) {
String url = getStatUrl(stats);
Object value = cache.get(url);
if (value != null) {
callback.onFetchStatsData((DataTable)value);
}
else {
super.fetchStatsData(stats,new FetchStatsDataCallback() {
#Override
public void onFetchStatsData(DataTable dataTable) {
cache.put(url,dataTable);
callback.onFetchStatsData(dataTable);
}
});
}
}
}
Basically the new class will lookup the value in the Cache and if it is not found it will call the fetch function in the parent class and intercept the callback to put it into the cache and then call the actual callback.
So in order to support both HTML5 localstorage and normal JS HashMap storage I created two implementations of my Cache interface:
JS HashMap storage:
public class DefaultcacheImpl implements Cache {
private HashMap<Object, Object> map;
public DefaultCacheImpl() {
this.map = new HashMap<Object, Object>();
}
#Override
public void put(Object key, Object value) {
if (key == null) {
throw new NullPointerException("key is null");
}
if (value == null) {
throw new NullPointerException("value is null");
}
map.put(key, value);
}
#Override
public Object get(Object key) {
// Check for null as Cache should not store null values / keys
if (key == null) {
throw new NullPointerException("key is null");
}
return map.get(key);
}
#Override
public void remove(Object key) {
map.remove(key);
}
#Override
public void clear() {
map.clear();
}
}
HTML5 localStorage:
public class LocalStorageImpl implements Cache{
public static enum TYPE {LOCAL,SESSION}
private TYPE type;
private Storage cacheStorage = null;
public LocalStorageImpl(TYPE type) throws Exception {
this.type = type;
if (type == TYPE.LOCAL) {
cacheStorage = Storage.getLocalStorageIfSupported();
}
else {
cacheStorage = Storage.getSessionStorageIfSupported();
}
if (cacheStorage == null) {
throw new Exception("LocalStorage not supported");
}
}
#Override
public void put(Object key, Object value) {
//Convert Object (could be any arbitrary object) into JSON
String jsonData = null;
if (value instanceof List) { // in case it is a list of Stat objects
JSONArray array = new JSONArray();
int index = 0;
for (Object val:(List)value) {
array.set(index,new JSONObject((JavaScriptObject)val));
index = index +1;
}
jsonData = array.toString();
}
else // in case it is a DataTable
{
jsonData = new JSONObject((JavaScriptObject) value).toString();
}
cacheStorage.setItem(key.toString(), jsonData);
}
#Override
public Object get(Object key) {
if (key == null) {
throw new NullPointerException("key is null");
}
String jsonDataString = cacheStorage.getItem(key.toString());
if (jsonDataString == null) {
return null;
}
Object data = null;
Object jsonData = JsonUtils.safeEval(jsonDataString);
if (!key.equals("list"))
data = DataTable.create((JavaScriptObject)data);
else if (jsonData instanceof JsArray){
JsArray<GenomeStat> jsonStats = (JsArray<GenomeStat>)jsonData;
List<GenomeStat> stats = new ArrayList<GenomeStat>();
for (int i = 0;i<jsonStats.length();i++) {
stats.add(jsonStats.get(i));
}
data = (Object)stats;
}
return data;
}
#Override
public void remove(Object key) {
cacheStorage.removeItem(key.toString());
}
#Override
public void clear() {
cacheStorage.clear();
}
public TYPE getType() {
return type;
}
}
The post got a little bit long but hopefully clarifies what I try to reach. It boils down to two questions:
Feedback on the design/architecture of this approach (for example subclassing RequestBilderDataSource for cache function, etc). Can this be improved (this is probably more related to general design than specifically GWT).
With the DefaultCacheImpl it is really easy to store and retrieve any arbitrary objects. How can I achieve the same thing with localStorage where I have to convert and parse JSON? I am using a DataTable which requires to call the DataTable.create(JavaScriptObject jso) function to work. How can I solve this without to many if/else and instance of checks?
My first thoughts: make it two layers of cache, not two different caches. Start with the in-memory map, so no serialization/deserialization is needed for reading a given object out, and so that changing an object in one place changes it in all. Then rely on the local storage to keep data around for the next page load, avoiding the need for pulling data down from the server.
I'd tend to say skip session storage, since that doesn't last long, but it does have its benefits.
For storing/reading data, I'd encourage checking out AutoBeans instead of using JSOs. This way you could support any type of data (that can be stored as an autobean) and could pass in a Class param into the fetcher to specify what kind of data you will read from the server/cache, and decode the json to a bean in the same way. As an added bonus, autobeans are easier to define - no JSNI required. A method could look something like this (note that In DataSource and its impl, the signature is different).
public <T> void fetch(Class<T> type, List<Stat> stats, Callback<T, Throwable> callback);
That said, what is DataTable.create? If it is already a JSO, you can just cast to DataTable as you (probably) normally do when reading from the RequestBuilder data.
I would also encourage not returning a JSON array directly from the server, but wrapping it in an object, as a best practice to protect your users' data from being read by other sites. (Okay, on re-reading the issues, objects aren't great either). Rather than discussing it here, check out JSON security best practices?
So, all of that said, first define the data (not really sure how this data is intended to work, so just making up as I go)
public interface DataTable {
String getTableName();
void setTableName(String tableName);
}
public interface Stat {// not really clear on what this is supposed to offer
String getKey();
void setKey(String key);
String getValue();
String setValue(String value);
}
public interface TableCollection {
List<DataTable> getTables();
void setTables(List<DataTable> tables);
int getRemaining();//useful for not sending all if you have too much?
}
For autobeans, we define a factory that can create any of our data when given a Class instance and some data. Each of these methods can be used as a sort of constructor to create a new instance on the client, and the factory can be passed to AutoBeanCodex to decode data.
interface DataABF extends AutoBeanFactory {
AutoBean<DataTable> dataTable();
AutoBean<Stat> stat();
AutoBean<TableCollection> tableCollection();
}
Delegate all work of String<=>Object to AutoBeanCodex, but you probably want some simple wrapper around it to make it easy to call from both the html5 cache and from the RequestBuilder results. Quick example here:
public class AutoBeanSerializer {
private final AutoBeanFactory factory;
public AutoBeanSerializer(AutoBeanFactory factory) {
this.factory = factory;
}
public String <T> encodeData(T data) {
//first, get the autobean mapped to the data
//probably throw something if we can't find it
AutoBean<T> autoBean = AutoBeanUtils.getAutoBean(data);
//then, encode it
//no factory or type needed here since the AutoBean has those details
return AutoBeanCodex.encode(autoBean);
}
public <T> T decodeData(Class<T> dataType, String json) {
AutoBean<T> bean = AutoBeanCodex.decode(factory, dataType, json);
//unwrap the bean, and return the actual data
return bean.as();
}
}