I am learning websockets and my webapp is using jsr 356 library. I followed the tutorials and I can encode/decode POJOs, however I can't find examples on how to serialize either arrays or collections to JSON.
This is what I am doing to encode my data:
#Override
public String encode(ScanPlus scan) throws EncodeException {
JsonObject jsonObject = createJsonObject(scan);
return jsonObject.toString();
}
private JsonObject createJsonObject(ScanPlus scan) {
JsonObject jsonObject = Json.createObjectBuilder()
.add("scan", scan.getCode())
.add("creationdate", String.valueOf(scan.getCreationDate()))
.add("username", scan.getUserName())
.build();
return jsonObject;
}
public String encode(ArrayList<ScanPlus> scans) throws EncodeException {
JsonArrayBuilder jsonArray = Json.createArrayBuilder();
for (ScanPlus scan : scans) {
JsonObject jsonObject = createJsonObject(scan);
jsonArray.add(jsonObject);
}
return jsonArray.toString();
}
This is how I send the data to the encoder:
#OnOpen
public void onOpen(Session session, #PathParam("username") String username) {
...
session.getBasicRemote().sendObject(scans);
}
And this is the exception I am getting:
javax.websocket.EncodeException: No encoder specified for object of class [class java.util.ArrayList]
Could anyone give me a hint on how to do it?
thanks
You need to create Encoder<ArrayList<ScanPlus>>; Encoder<ScanPlus> is not enough..
Related
I am new to testing and i am trying to write a unit test cases on a Flink Datastream which takes input a jsonobject and passes the json object to a processfuntion and it returns a valid or invalid jsonobject when certain rule conditions are met below is the junit test case, below i am trying to compare the output jsonobject from process function with the jsonobject of the input file
#Test
public void testcompareInputAndOutputDataJSONSignal() throws Exception {
org.json.JSONObject jsonObject = toJsonObject();
String input = jsonObject.toString();
String output = JSONDataStreamOutput();
assertEquals(mapper.readTree(input), mapper.readTree(output));
}
below is my toJSONObject and JSONDataStream meathods
public static JSONObject toJsonObject() throws IOException, ParseException {
JSONParser jsonParser = new JSONParser();
FileReader fileReader = new FileReader(getFileFromResources("input.json"));
JSONObject obj = (JSONObject) jsonParser.parse(fileReader);
return obj;
}
public String SignalDataStreamOutput() throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<JSONObject> validSignal = env.fromElements(toJsonObject())
.process(new JsonFilter());
String outputFolder = "output";
validSignal.writeAsText(outputFolder).setParallelism(1);
env.execute();
String content = new String(Files.readAllBytes(Paths.get("output.txt")));
return content;
}
What i am doing is i am converting a jsonfile to jsonobject using the toJSONObject method and sending to a data stream using SignalDataStreamOutput method which will intern send it to a process function in JsonFilter class and validate it against a set of rules and if it's valid it will return a jsonobject and when trying to access the jsonobject directly from stream i am getting value like org.apache.flink#994jdkeiri so i am trying to write the output to a file and trying to read it back to a string and comparing it in test method but this is a work around process and i found a link to use Mockito framework here i changed it to use json object like below
final Collector<JSONObject> collectorMock = (Collector<JSONObject>)Mockito.mock(JsonFilter.class);
final Context contextMock = Mockito.mock(Context.class);
#Test
public void testcompareInputAndOutputDataForValidSignal() throws Exception {
org.json.JSONObject jsonObject = convertToJsonObject();
Mockito.verify(collectorMock).collect(jsonObject);
}
but the above approach is also not working can you suggest me simplified approach to test the json object
I'm using rest-assured and twitter4j for testing twitter API.
All calls are made via RestAssured, twitter4j is used for Json response deserialization.
And what I want to do is to deserialize Json response from twitter - GET statuses/home_timeline which returns Array of Status objects(from twitter4j).
I can easily deserialize one Status object like here:
#Test
public void verifyTwitCreation() {
RequestSpecification spec = new RqBuilder()
.withStatus(textToPublish)
.build();
Response response = twitClient.createTwit(spec);
assertResponseCode(response, 200);
String json = response.getBody().asString();
Status status = null;
try {
status = TwitterObjectFactory.createStatus(json);
} catch (TwitterException e) {
e.printStackTrace();
}
System.out.println(status.toString());
}
But I don't know how to do the same for deserializing array of such Status objects.
Try extracting a list of statuses using JsonPath and then parse them using TwitterObjectFactory:
Response response = twitClient.createTwit(spec);
List<Map<Object, Object>> responseList = response.jsonPath().getList("$");
ObjectMapper mapper = new ObjectMapper();
List<Status> statuses = responseList.stream().map(s -> {
Status status = null;
try {
String json = mapper.writeValueAsString(s)
status = TwitterObjectFactory.createStatus(json);
} catch (TwitterException | IOException e) {
e.printStackTrace();
}
return status;
}).collect(Collectors.toList());
You could move try/catch during parsing to a separate method so it looks nicer:
public class TestClass {
#Test
public void verifyTwitCreation() {
RequestSpecification spec = new RqBuilder()
.withStatus(textToPublish)
.build();
Response response = twitClient.createTwit(spec);
List<Map<Object, Object>> responseList = response.jsonPath().getList("$");
List<Status> statuses = responseList.stream().map(TestClass::createStatus)
.collect(Collectors.toList());
}
private static Status createStatus(Map<Object, Object> jsonMap) {
Status status = null;
ObjectMapper mapper = new ObjectMapper();
try {
String json = mapper.writeValueAsString(jsonMap);
status = TwitterObjectFactory.createStatus(json);
} catch (TwitterException | IOException e) {
e.printStackTrace();
}
return status;
}
}
Update:
Since JsonPath getList() returns list of maps we should convert all maps to JSON string so that it can be used by TwitterObjectFactory. Jackson's ObjectMapper is used in example, but any JSON parsing tool can be used.
I kinda solve my problem. I just generated POJOs from Json with this plugin - https://plugins.jetbrains.com/plugin/8634-robopojogenerator and than mapped Json with rest-assured
List<Status> statuses = Arrays.asList(response.as(Status[].class));
But still I will appreciate the answer for solution with using twitter4j
I have a JSON which sends array of element in normal cases but sends empty string "" tag without array [] brackets in case of 0 elements.
How to handle this with Gson? I want to ignore the error and not cause JSONParsingException.
eg.
"types": [
"Environment",
"Management",
"Computers"
],
sometimes it returns:
"types" : ""
Getting the following exception: Expected BEGIN ARRAY but was string
Since you don't have control over the input JSON string, you can test the content and decide what to do with it.
Here is an example of a working Java class:
import com.google.gson.Gson;
import java.util.ArrayList;
public class Test {
class Types {
Object types;
}
public void test(String input) {
Gson gson = new Gson();
Types types = gson.fromJson(input,Types.class);
if(types.types instanceof ArrayList) {
System.out.println("types is an ArrayList");
} else if (types.types instanceof String) {
System.out.println("types is an empty String");
}
}
public static void main(String[] args) {
String input = "{\"types\": [\n" +
" \"Environment\",\n" +
" \"Management\",\n" +
" \"Computers\"\n" +
" ]}";
String input2 = "{\"types\" : \"\"}";
Test testing = new Test();
testing.test(input2); //change input2 to input
}
}
If a bad JSON schema is not under your control, you can implement a specific type adapter that would try to determine whether the given JSON document is fine for you and, if possible, make some transformations. I would recomment to use #JsonAdapter in order to specify improperly designed types (at least I hope the entire API is not improperly designed).
For example,
final class Wrapper {
#JsonAdapter(LenientListTypeAdapterFactory.class)
final List<String> types = null;
}
where LenientListTypeAdapterFactory can be implemented as follows:
final class LenientListTypeAdapterFactory
implements TypeAdapterFactory {
// Gson can instantiate it itself, let it just do it
private LenientListTypeAdapterFactory() {
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Obtaining the original list type adapter
#SuppressWarnings("unchecked")
final TypeAdapter<List<?>> realListTypeAdapter = (TypeAdapter<List<?>>) gson.getAdapter(typeToken);
// And wrap it up in the lenient JSON type adapter
#SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new LenientListTypeAdapter(realListTypeAdapter);
return castTypeAdapter;
}
private static final class LenientListTypeAdapter
extends TypeAdapter<List<?>> {
private final TypeAdapter<List<?>> realListTypeAdapter;
private LenientListTypeAdapter(final TypeAdapter<List<?>> realListTypeAdapter) {
this.realListTypeAdapter = realListTypeAdapter;
}
#Override
public void write(final JsonWriter out, final List<?> value)
throws IOException {
realListTypeAdapter.write(out, value);
}
#Override
public List<?> read(final JsonReader in)
throws IOException {
// Check the next (effectively current) JSON token
switch ( in.peek() ) {
// If it's either `[...` or `null` -- we're supposing it's a "normal" list
case BEGIN_ARRAY:
case NULL:
return realListTypeAdapter.read(in);
// Is it a string?
case STRING:
// Skip the value entirely
in.skipValue();
// And return a new array list.
// Note that you might return emptyList() but Gson uses mutable lists so we do either
return new ArrayList<>();
// Not anything known else?
case END_ARRAY:
case BEGIN_OBJECT:
case END_OBJECT:
case NAME:
case NUMBER:
case BOOLEAN:
case END_DOCUMENT:
// Something definitely unexpected
throw new MalformedJsonException("Cannot parse " + in);
default:
// This would never happen unless Gson adds a new type token
throw new AssertionError();
}
}
}
}
Here is it how it can be tested:
for ( final String name : ImmutableList.of("3-elements.json", "0-elements.json") ) {
try ( final Reader reader = getPackageResourceReader(Q43562427.class, name) ) {
final Wrapper wrapper = gson.fromJson(reader, Wrapper.class);
System.out.println(wrapper.types);
}
}
Output:
[Environment, Management, Computers]
[]
If the entire API uses "" for empty arrays, then you can drop the #JsonAdapter annotation and register the LenientListTypeAdapterFactory via GsonBuilder, but add the following lines to the create method in order not to break other type adapters:
if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {
// This tells Gson to try to pick up the next best-match type adapter
return null;
}
...
There are a lot of weirdly designed JSON response choices, but this one hits the top #1 issue where nulls or empties are represented with "". Good luck!
Thanks for all your answers.
The recommed way as mentioned in above answers would be to use TypeAdapters and ExclusionStrategy for GSON.
Here is a good example Custom GSON desrialization
i am using jackson to parse data i am using following code to parse json
public Map<String, Object> savePreference(#RequestBody Map map) throws IOException{
List preferenceDetails = (List) map.get("data");
int preferenceIndex = 0;
while(preferenceIndex < preferenceDetails.size()){
final ObjectMapper mapper = new ObjectMapper();
System.out.println(preferenceDetails.get(preferenceIndex));
mapper.readValue(preferenceDetails.get(preferenceIndex).toString(), Preference.class);
preferenceIndex++;
}
return null;
}
i am sending json from client side like this
{"data":[
{
"preferenceType":"Travelling"
},
{
"preferenceType":"Shopping"
}
]
}
but above code throws exception when i called
mapper.readValue(preferenceDetails.get(preferenceIndex).toString(), Preference.class);
exception is
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('p' (code 112)): was expecting double-quote to start field name
i am printing preference details in while loop
{preferenceType=Travelling}
You do not need to parse line by line. Using objects directly will be much more easier. Here is a test case which demonstrates how to parse your json to a object directly.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import java.util.Collection;
public class JacksonTest {
#Test
public void testName() throws Exception {
final String test = "{\"data\":[\n" +
" {\n" +
" \"preferenceType\":\"Travelling\"\n" +
" },\n" +
" {\n" +
" \"preferenceType\":\"Shopping\"\n" +
" }\n" +
" ]\n" +
"}";
final ObjectMapper objectMapper = new ObjectMapper();
final Data data = objectMapper.readValue(test, Data.class);
Assert.assertNotNull(data);
Assert.assertEquals(2, data.getData().size());
}
static class Data {
private Collection<PreferenceType> data;
public Collection<PreferenceType> getData() {
return data;
}
public void setData(Collection<PreferenceType> data) {
this.data = data;
}
}
static class PreferenceType {
private String preferenceType;
public String getPreferenceType() {
return preferenceType;
}
public void setPreferenceType(String preferenceType) {
this.preferenceType = preferenceType;
}
}
}
After that you can build your own map how ever you want. But your framework may be able to handle this kind of request. You do not need to parse it manually. You should try getting Data object directly, instead of getting your body as Map.
Update
Try something like this. But first define Data class properly ( at least not as an inner class).
public Map<String, Object> savePreference(#RequestBody Data data) throws IOException{
...
}
Update 2
And also your map already has your object PreferenceDetail. It may already handle the json and map it as object. Please debug and check if your map.get("data") returns List<PreferenceDetail> or not. If it returns List there is no more work is needed to parse json using jackson. You can simply do something like this.
public Map<String, Object> savePreference(#RequestBody Map map) throws IOException{
List<PreferenceDetail> preferenceDetails = (List) map.get("data");
for (PreferenceDetail preferenceDetail : preferenceDetails) {
System.out.println(preferenceDetail.getPreferenceType());
}
return null;
}
I solved my problem by using jackson object mapper.
public Map<String, Object> savePreference(#RequestBody Map map) throws IOException{
log.debug("saving preferences");
if(preferenceService.getPreferencesByUser() != null && preferenceService.getPreferencesByUser().size() != 0)
return ResponseHandler.generateResponse(configProp.getProperty("user.preference.exist"), HttpStatus.ACCEPTED, true, null);
final ObjectMapper mapper = new ObjectMapper();
List preferenceDetails = (List) map.get("data");
int preferenceIndex = 0;
while(preferenceIndex < preferenceDetails.size()){
preferenceService.savePreference(mapper.readValue(mapper.writeValueAsString(preferenceDetails.get(preferenceIndex)), Preference.class));
preferenceIndex++;
}
return ResponseHandler.generateResponse(configProp.getProperty("preference.added"), HttpStatus.ACCEPTED, true, null);
}
I'm working on a little app and using GWT to build it.
I just tried making a request to a remote server which will return a response as JSON.
I've tried using the overlay types concept but I couldn't get it working. I've been changing the code around so its a bit off from where the Google GWT tutorials left.
JavaScriptObject json;
public JavaScriptObject executeQuery(String query) {
String url = "http://api.domain.com?client_id=xxxx&query=";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
URL.encode(url + query));
try {
#SuppressWarnings("unused")
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
// violation, etc.)
}
public void onResponseReceived(Request request,
Response response) {
if (200 == response.getStatusCode()) {
// Process the response in response.getText()
json =parseJson(response.getText());
} else {
}
}
});
} catch (RequestException e) {
// Couldn't connect to server
}
return json;
}
public static native JavaScriptObject parseJson(String jsonStr) /*-{
return eval(jsonStr );
;
}-*/;
In the chrome's debugger I get umbrellaexception, unable to see the stack trace and GWT debugger dies with NoSuchMethodError... Any ideas, pointers?
You may have a look to GWT AutoBean framework.
AutoBean allow you to serialize and deserialize JSON string from and to Plain Old Java Object.
For me this framework became essential :
Code is cleaner than with JSNI objects (JavaScript Native Interface)
No dependancy with Framework not supported by Google (like RestyGWT)
You just define interfaces with getters and setters :
// Declare any bean-like interface with matching getters and setters,
// no base type is necessary
interface Person {
Address getAddress();
String getName();
void setName(String name):
void setAddress(Address a);
}
interface Address {
String getZipcode();
void setZipcode(String zipCode);
}
Later you can serialize or deserialize JSON String using a factory (See documentation) :
// (...)
String serializeToJson(Person person) {
// Retrieve the AutoBean controller
AutoBean<Person> bean = AutoBeanUtils.getAutoBean(person);
return AutoBeanCodex.encode(bean).getPayload();
}
Person deserializeFromJson(String json) {
AutoBean<Person> bean = AutoBeanCodex.decode(myFactory, Person.class, json);
return bean.as();
}
// (...)
First post on Stack Overflow (!) : I hope this help :)
Use JsonUtils#safeEval() to evaluate the JSON string instead of calling eval() directly.
More importantly, don't try to pass the result of an asynchronous call (like RequestBuilder#sendRequest() back to a caller using return - use a callback:
public void executeQuery(String query,
final AsyncCallback<JavaScriptObject> callback)
{
...
try {
builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable caught) {
callback.onFailure(caught);
}
public void onResponseReceived(Request request, Response response) {
if (Response.SC_OK == response.getStatusCode()) {
try {
callback.onSuccess(JsonUtils.safeEval(response.getText()));
} catch (IllegalArgumentException iax) {
callback.onFailure(iax);
}
} else {
// Better to use a typed exception here to indicate the specific
// cause of the failure.
callback.onFailure(new Exception("Bad return code."));
}
}
});
} catch (RequestException e) {
callback.onFailure(e);
}
}
Generally, the workflow you're describing consists of four steps:
Make the request
Receive the JSON text
Parse the JSON in JavaScript objects
Describe these JavaScript objects using an overlay type
It sounds like you've already got steps 1 and 2 working properly.
Parse the JSON
JSONParser.parseStrict will do nicely. You'll be returned a JSONValue object.
This will allow you to avoid using your custom native method and will also make sure that it prevents arbitrary code execution while parsing the JSON. If your JSON payload is trusted and you want raw speed, use JSONParser.parseLenient. In either case, you need not write your own parser method.
Let's say that you're expecting the following JSON:
{
"name": "Bob Jones",
"occupations": [
"Igloo renovations contractor",
"Cesium clock cleaner"
]
}
Since you know that the JSON describes an object, you can tell the JSONValue that you're expecting to get a JavaScriptObject.
String jsonText = makeRequestAndGetJsonText(); // assume you've already made request
JSONValue jsonValue = JSONParser.parseStrict(jsonText);
JSONObject jsonObject = jsonValue.isObject(); // assert that this is an object
if (jsonObject == null) {
// uh oh, it wasn't an object after
// do error handling here
throw new RuntimeException("JSON payload did not describe an object");
}
Describe as an overlay type
Now that you know that your JSON describes an object, you can get that object and describe it in terms of a JavaScript class. Say you have this overlay type:
class Person {
String getName() /*-{
return this.name;
}-*/;
JsArray getOccupations() /*-{
return this.occupations;
}-*/;
}
You can make your new JavaScript object conform to this Java class by doing a cast:
Person person = jsonObject.getJavaScriptObject().cast();
String name = person.getName(); // name is "Bob Jones"
Using eval is generally dangerous, and can result in all kinds of strange behavior, if the server returns invalid JSON (note, that it's necessary, that the JSON top element is an array, if you simply use eval(jsonStr)!). So I'd make the server return a very simple result like
[ "hello" ]
and see, if the error still occurs, or if you can get a better stack trace.
Note: I assume, that the server is reachable under the same URL + port + protocol as your GWT host page (otherwise, RequestBuilder wouldn't work anyway due to Same Origin Policy.)
You actually don't need to parse the JSON, you can use native JSNI objects (JavaScript Native Interface).
Here's an example I pulled from a recent project doing basically the same thing you're doing:
public class Person extends JavaScriptObject{
// Overlay types always have protected, zero argument constructors.
protected Person(){}
// JSNI methods to get stock data
public final native String getName() /*-{ return this.name; }-*/;
public final native String getOccupation() /*-{ return this.occupation; }-*/;
// Non-JSNI methods below
}
and then to retrieve it like so:
/**
* Convert the string of JSON into JavaScript object.
*
*/
private final native JsArray<Person> asArrayOfPollData(String json) /*-{
return eval(json);
}-*/;
private void retrievePeopleList(){
errorMsgLabel.setVisible(false);
String url = JSON_URL;
url = URL.encode(url);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
try{
#SuppressWarnings("unused")
Request request = builder.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request req, Response resp) {
if(resp.getStatusCode() == 200){
JsArray<Person> jsonPeople = asArrayOfPeopleData(resp.getText());
populatePeopleTable(people);
}
else{
displayError("Couldn't retrieve JSON (" + resp.getStatusText() + ")");
}
}
#Override
public void onError(Request req, Throwable arg1) {
System.out.println("couldn't retrieve JSON");
displayError("Couldn't retrieve JSON");
}
});
} catch(RequestException e) {
System.out.println("couldn't retrieve JSON");
displayError("Couldn't retrieve JSON");
}
}
So essentially you're casting the response as an array of JSON Objects. Good stuff.
More info here: http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html