I'm currently making a class that tests if the gson classes I generated are still up to date, this means I have to detect if any of the json elements have changed, deleted or added. Let's say I have a json which is simply
{
"FloatingText": {
"Dodge_Enabled": false,
"foo": false
},
"General": {
"something": false,
"somethingelse": false
}
}
And the class for floatingtext has the following fields:
#SerializedName("Dodge_Enabled")
#Expose
public Boolean dodgeEnabled;
Now the foo field is obviously missing here, my question is how to find out how I can detect this reliably even when the json is about a thousand times bigger. The gson documentation states that it should throw a exception when the json doesn't match the class but it doesn't and after some research it only throws it when the type is heavily malformed so that's off the table.
The code I have now checks correctly for missing or changed fields but not for new ones
GameSettings gameSettings = gson.fromJson(json, GameSettings.class);
for (Field declaredField : gameSettings.getClass().getDeclaredFields()) {
declaredField.setAccessible(true);
Object object = declaredField.get(gameSettings);
if (object == null && !Modifier.isStatic(declaredField.getModifiers())) {
Assert.fail();
}
for (Field declaredSubfield : object.getClass().getDeclaredFields()) {
declaredSubfield.setAccessible(true);
Object subObject = declaredSubfield.get(object);
if (subObject == null && !Modifier.isStatic(declaredSubfield.getModifiers())) {
System.out.println("Processing went wrong at: " + declaredSubfield + "\n Json: " + json);
Assert.fail();
}
}
}
One thing you could do is to deserialize the incoming JSON, then serialize it again and check if the result matches your incoming JSON:
GameSettings settings = gson.fromJson( json, GameSettings.class );
String exportedJson = gson.toJson( settings );
if ( !exportedJson.equals( json ) )
{
Assert.fail();
}
This would of course be prone to raise alarms if someone modified the previously exported JSON by hand and changed something about the pretty-printing. But if it's just about raising an alert for the dev to check, it might be enough.
If you wanted to output more information about what exactly is different in the external representation, you could use a library that detects diffs:
https://github.com/java-diff-utils/java-diff-utils
https://github.com/google/diff-match-patch
Related
I have an api for which I am receiving a Java pojo request in json format:
{
"migrationId" : "32n2342342j";
"someDynamicField" : {"A" : "",
"B" : ""}
}
This someDynamicField can have different property name for which I need to extra the data from.
I cannot have a class for this model since that field is dynamic. But for sure that someDynamicField will be of string type but name will be different.
One thing is for sure that someDynamicField can be one of the string from the finite set for eg: ["move", "source"....] and this set will grow in future.
For example :
{
"migrationId" : "32n2342342j";
"move" : {"sourHost" : "",
"targetHost" : ""}
}
sometime it can be
{
"migrationId" : "32n2342342j";
"delete" : {"sourHost" : "",
"targetHost" : ""}
}
What would be the best way to extract this information from the request?
I tried to look at this one : How to map dynamic JSON in JAX-RS Here they use the vairable name explicitly and in my case the field is dynamic?
I would take a String instead of an Object and parse it with Jackson. This isn't the complete code but something like:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
#Path("/blah")
public Response myMethod(String inputObject) {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(inputObject);
String migrationId = jsonNode.get("migrationId");
if( jsonNode.has("move") ) {
handleMoveNode(jsonNode.get("move"));
}
else if( jsonNode.has("delete") ) {
handleDeleteNode(jsonNode.get("delete"));
}
// handle the other node types
return Response.ok().build();
}
The point is to take a String in your JAX-RS method and parse it yourself.
I want to check the existence of nested key in Video object returned as a Json response from youtube video search by using below code:-
YouTube.Videos.List searchvideostats = youtube.videos().list("snippet,statistics");
searchvideostats.setKey(apiKey);
Video v = searchvideostats.execute().getItems().get(0);
System.out.println(v.toPrettyString());
I got output like this:-
{
"etag" : "\"m2yskBQFythfE4irbTIeOgYYfBU/-TONXAYMx_10Caihcoac4XCyb4I\"",
"id" : "4Aa9GwWaRv0",
"kind" : "youtube#video",
"snippet" : {
"categoryId" : "10",
...................
.........
MY goal is:- how to check whether categoryId key is present in this response or not. coz if do v.getSnippet().getCategoryId() it gives NullPointerException if categotyId is not present in Json.
Tried:-
if (v.containsKey("id")) {
System.out.println("contains");
} else {
System.out.println("doesnt contains");
}
this returns contains as expected.
if (v.containsKey("categoryId")) {
System.out.println("contains");
} else {
System.out.println("doesnt contains");
}
This returns doesnt contains.. which is not expected. How would I check if this nested key is available?
P.S. -> I have to check many nested such keys.
Thanks for help.
You don't need String manipulations. Just use Youtube library.
if(video.getSnippet()!=null && video.getSnippet().getCategoryId()!=null)
will do the stuff.
note: checking for zero length categoryid might be necessary also.
I'm using the Azure SDK, which uses Gson to serialize and deserialize objects to upload to their Mobile Services API. I've had success doing this with a customs class of primitives only, as in the examples given in the Gson User Guide. I'd like to do this with a custom class that contains an ArrayList. I'd even settle for a List or an Array, I'm not too picky. Here's my class:
public class clsUser {
private int UserID;
private String UserName;
private String UserStatus;
public ArrayList<String> UserEmails;
Gson appears to serialize the class when sending to the server this way:
{ UserEmails: [ 'myEmail#gmail.com', 'myEmail2#yahoo.com' ],
UserStatus: 'A',
UserName: 'Scott',
UserID: 1 }
On the server, I'm storing it all in a relational SQLServer database, so I'm keeping UserEmails as a String in there, and trying to bring it back out as an array.
However back on my Android/Gson/Client side, I'm getting an error:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
I suspect that the problem is that SQLServer is returning UserEmails with surrounding quotes. I'm not sure how to fix this. Complicating matters is that the Gson implementation is inside the Azure SDK, so I don't think I could even write a custom deserializer if I wanted to. Any suggestions on fixing this? Thanks in advance!
Konrad's comment helped me past this stumbling block. If the problem is that the server is returning this field as a String, then all I needed to do was insert a step to convert it to an Array before returning the data to my client. (Yay for dynamic types in Javascript!) My updated server script is below:
function lookup(request, response) {
var UserID = request.query.UserID;
if (UserID == null || isNaN(parseFloat(UserID))) {
response.send(400, "Invalid Parameters (" + UserID + ")");
} else {
request.service.mssql.query("select ID UserID, Full_Name UserName, Email_Addresses UserEmails, Status UserStatus from User_List where ID=?;", [UserID], {
success: function (results) {
if (results.length > 0) {
//*** Added this line below to convert String back to Array ***//
results[0].UserEmails = results[0].UserEmails.split(',')
response.send(200, results[0]);
} else {
response.send(200, []);
}
}, error: function (err) {
response.send(500, {"Message" : "Lookup error = " + err});
}
});
}
}
I have to refactor some areas of my app to use the streaming API in Gson, but very quickly I'm running into a strange problem I'm not sure how to get around. The following constructor on my class receives a JsonReader and is supposed to loop through the properties of the object. LogCat shows the name of the first property output, then an exception "Expected a name but was BOOLEAN". I only asked for the name using reader.nextName(). What gives?
JSON Object:
{
"IsActive":true,
"LocationName":"Denver",
...
}
Class constructor:
public AppLocation(JsonReader reader){
try {
reader.beginObject();
while(reader.hasNext()){
final String pName = reader.nextName();
final boolean isNull = reader.peek() == JsonToken.NULL;
if(!isNull){
Log.d("MENET", pName);
}else{
reader.skipValue();
}
}
reader.endObject();
} catch (IOException e) {
Log.e("MENET", e.getMessage());
}
}
The streaming method of using this reader works with "elements" i.e. names or values.
So after the first "element", which is a name, you would get a value.
Except your code is calling reader.nextName() which is why it says "Expected a name..."
There is a good example on the Android site under JsonReader:
http://developer.android.com/reference/android/util/JsonReader.html
I'm using XStream and JETTISON's Stax JSON serializer to send/receive messages to/from JSON javascripts clients and Java web applications.
I want to be able to create a list of objects to send to the server and be properly marshalled into Java but the format that XStream and JSON expect it in is very non-intuitive and requires our javascript libraries to jump through hoops.
[EDIT Update issues using GSON library]
I attempted to use the GSON library but it cannot deserialize concrete objects when I only have it expect generic super classes (XStream and Jettison handles this because type information is baked into the serialization).
GSON FAQ states Collection Limitation:
Collections Limitations
Can serialize collection of arbitrary objects but can not deserialize from it
Because there is no way for the user to indicate the type of the resulting object
While deserializing, Collection must be of a specific generic type
Maybe I'm using bad java practices but how would I go about building a JSON to Java messaging framework that sent/received various concrete Message objects in JSON format?
For example this fails:
public static void main(String[] args) {
Gson gson = new Gson();
MockMessage mock1 = new MockMessage();
MockMessage mock2 = new MockMessage();
MockMessageOther mock3 = new MockMessageOther();
List<MockMessage> messages = new ArrayList<MockMessage>();
messages.add(mock1);
messages.add(mock2);
messages.add(mock3);
String jsonString = gson.toJson(messages);
//JSON list format is non-intuitive single element array with class name fields
System.out.println(jsonString);
List gsonJSONUnmarshalledMessages = (List)gson.fromJson(jsonString, List.class);
//This will print 3 messages unmarshalled
System.out.println("XStream format JSON Number of messages unmarshalled: " + gsonJSONUnmarshalledMessages.size());
}
[{"val":1},{"val":1},{"otherVal":1,"val":1}]
Exception in thread "main" com.google.gson.JsonParseException: The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#638bd7f1 failed to deserialized json object [{"val":1},{"val":1},{"otherVal":1,"val":1}] given the type interface java.util.List
Here's an example, I want to send a list of 3 Message objects, 2 are of the same type and the 3rd is a different type.
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
class MockMessage {
int val = 1;
}
class MockMessageOther {
int otherVal = 1;
}
public class TestJSONXStream {
public static void main(String[] args) {
JettisonMappedXmlDriver xmlDriver = new JettisonMappedXmlDriver();
XStream xstream = new XStream(xmlDriver);
MockMessage mock1 = new MockMessage();
MockMessage mock2 = new MockMessage();
MockMessageOther mock3 = new MockMessageOther();
List messages = new ArrayList();
messages.add(mock1);
messages.add(mock2);
messages.add(mock3);
String jsonString = xstream.toXML(messages);
//JSON list format is non-intuitive single element array with class name fields
System.out.println(jsonString);
List xstreamJSONUnmarshalledMessages = (List)xstream.fromXML(jsonString);
//This will print 3 messages unmarshalled
System.out.println("XStream format JSON Number of messages unmarshalled: " + xstreamJSONUnmarshalledMessages.size());
//Attempt to deserialize a reasonable looking JSON string
String jsonTest =
"{"+
"\"list\" : ["+
"{"+
"\"MockMessage\" : {"+
"\"val\" : 1"+
"}"+
"}, {"+
"\"MockMessage\" : {"+
"\"val\" : 1"+
"}"+
"}, {"+
"\"MockMessageOther\" : {"+
"\"otherVal\" : 1"+
"}"+
"} ]"+
"};";
List unmarshalledMessages = (List)xstream.fromXML(jsonTest);
//We expect 3 messages but XStream only deserializes one
System.out.println("Normal format JSON Number of messages unmarshalled: " + unmarshalledMessages.size());
}
}
Intuitively I expect the XStream JSON to be serialized (and able to deserialize correctly) from the following format:
{
"list" : [
{
"MockMessage" : {
"val" : 1
}
}, {
"MockMessage" : {
"val" : 1
}
}, {
"MockMessageOther" : {
"otherVal" : 1
}
} ]
}
Instead XStream creates a single element list with fields that are named the classnames and nested arrays of Objects of the same type.
{
"list" : [ {
"MockMessage" : [ {
"val" : 1
}, {
"val" : 1
} ],
"MockMessageOther" : {
"otherVal" : 1
}
} ]
}
The trouble may be caused by it using the XStream XML CollectionConverter?
Does anyone have a suggestion for a good JSON Java object serialization that allows you to read/write arbitrary Java objects. I looked at the Jackson Java JSON Processor but when you were reading in objects from a stream you had to specify what type of object it was unlike XStream where it will read in any object (because the serialized XStream JSON contains class name information).
I agree with other poster in that XStream is not a good fit -- it's an OXM (Object/Xml Mapper), and JSON is handled as a secondary output format using XML processing path. This is why a "convention" (of how to convert hierarchich xml model into object-graph model of json and vice versa) is needed; and your choice boils down to using whatever is least intrusive of sub-optimal choices.
That works ok if XML is your primary data format, and you just need some rudimentary JSON(-like) support.
To get good JSON-support, I would consider using a JSON processing library that does real OJM mapping (I assume Svenson does too, but additionally), such as:
Jackson
Google-gson
Also: even if you do need to support both XML and JSON, you are IMO better off using separate libraries for these tasks -- objects (beans) to use on server-side need not be different, just serialization libs that convert to/from xml and json.
I realize this is off-topic, but I'd like to present a solution in svenson JSON.
Do you really need public fields in your domain classes? Apart from having to use properties, svenson can handle cases like this with a more simple JSON output with a discriminator property
class Message
{
// .. your properties with getters and setters ..
// special property "type" acts a signal for conversion
}
class MessageOther
{
...
}
List list = new ArrayList();
list.add(new Message());
list.add(new MessageOther());
list.add(new Message());
String jsonDataSet = JSON.defaultJSON().forValue(list);
would output JSON like
[
{"type":"message", ... },
{"type":"message_other", ... },
{"type":"message", ... }
]
which could be parsed again with code like this
// configure reusable parse instance
JSONParser parser = new JSONParser();
// type mapper to map to your types
PropertyValueBasedTypeMapper mapper = new PropertyValueBasedTypeMapper();
mapper.setParsePathInfo("[]");
mapper.addFieldValueMapping("message", Message.class);
mapper.addFieldValueMapping("message_other", MessageOther.class);
parser.setTypeMapper(mapper);
List list = parser.parse(List.class, jsonDataset);
A svenson type mapper based on the full class name would look something like this
public class ClassNameBasedTypeMapper extends PropertyValueBasedTypeMapper
{
protected Class getTypeHintFromTypeProperty(String value) throws IllegalStateException
{
try
{
return Class.forName(value);
}
catch (ClassNotFoundException e)
{
throw new IllegalStateException(value + " is no valid class", e);
}
}
}
which is not an ideal implementation as it inherits the configuration of PropertyValueBasedTypeMapper without really needing. (should include a cleaner version in svenson)
The setup is very much like above
JSONParser parser = new JSONParser();
ClassNameBasedTypeMapper mapper = new ClassNameBasedTypeMapper();
mapper.setParsePathInfo("[]");
parser.setTypeMapper(mapper);
List foos = parser
.parse( List.class, "[{\"type\":\"package.Foo\"},{\"type\":\"package.Bar\"}]");