I am a beginner in junit I would like to test two JsonObject with dynamic data especially the date.
I have a proprety "CreationDate", In the first json I use the localDateTime "now()" on the other hand the jsonTest file contains a literal date.
JsonObject actual = (JsonObject) parser.parse(myListResponse.readEntity(String.class));
JsonObject expected = (JsonObject) parser.parse(fixture("path/jsonTest.json"));
assertThat(actual , is(expected));
How I can resolve this to get a valid test
You have to create a custom Matcher by extending BaseMatcher. Then you can do the comparison/matching by using the desired fields.
For example:
static class JsonObjectMatcher extends BaseMatcher<JsonObject> {
private final JsonObject target;
public static JsonObjectMatcher customMatchesTheJson(final JsonObject expected) {
return new JsonObjectMatcher(expected);
}
private JsonObjectMatcher(JsonObject target) {
this.target = target;
}
#Override
public boolean matches(Object o) {
if (o instanceof JsonObject) {
final JsonObject other = (JsonObject) o;
// TODO: 5/11/21 Custom compare target to other
}
return false;
}
#Override
public void describeTo(Description description) {
// TODO: 5/11/21 Add to the description
}
}
And you can use it to match as follows:
assertThat(actual, JsonObjectMatcher.customMatchesTheJson(expected));
Related
I would like to remove empty string by using toJson from GSON.
Example object:
public class ExampleObject {
String defaultEmpty = "";
String example;
public ExampleObject() {
this.example = "foo";
}
and after
using
new Gson().toJson(new ExampleObject());
I am receiving
"defaultEmpty " : "",
"position" : "foo"
Is there any way to not including empty string during deserialization? I know GSON is ignoring null, but sometimes I have an empty string in my object and I have to ignore it.
Here is a simplified example.
static class ExampleObjectSerializer implements JsonSerializer<ExampleObject> {
#Override
public JsonElement serialize(ExampleObject src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.addProperty("example", src.example);
if(src.defaultEmpty != null && !src.defaultEmpty.equals("")) {
object.addProperty("defaultEmpty", src.defaultEmpty);
}
return object;
}
}
public static void main(String[] args) {
Gson gson = new GsonBuilder().registerTypeAdapter(ExampleObject.class, new ExampleObjectSerializer()).create();
ExampleObject exampleObject = new ExampleObject();
String result = gson.toJson(exampleObject);
System.out.println(result);
}
I have requirement where I need to convert java object to json.
I am using Gson for that but i need the converter to only serialize the non null or not empty values.
For example:
//my java object looks like
class TestObject{
String test1;
String test2;
OtherObject otherObject = new OtherObject();
}
now my Gson instance to convert this object to json looks like
Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
String jsonStr = gson.toJson(obj);
println jsonStr;
In the above print, the result is
{"test1":"test1", "test2":"", "otherObject":{}}
Here i just wanted the result to be
{"test1":"test1"}
Since the test2 is empty and otherObject is empty, i don't want them to be serialized to json data.
Btw, I am using Groovy/Grails so if there is any plugin for this that would be good, if not any suggestion to customize the gson serialization class would be good.
Create your own TypeAdapter
public class MyTypeAdapter extends TypeAdapter<TestObject>() {
#Override
public void write(JsonWriter out, TestObject value) throws IOException {
out.beginObject();
if (!Strings.isNullOrEmpty(value.test1)) {
out.name("test1");
out.value(value.test1);
}
if (!Strings.isNullOrEmpty(value.test2)) {
out.name("test2");
out.value(value.test1);
}
/* similar check for otherObject */
out.endObject();
}
#Override
public TestObject read(JsonReader in) throws IOException {
// do something similar, but the other way around
}
}
You can then register it with Gson.
Gson gson = new GsonBuilder().registerTypeAdapter(TestObject.class, new MyTypeAdapter()).create();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
System.out.println(gson.toJson(obj));
produces
{"test1":"test1"}
The GsonBuilder class has a bunch of methods to create your own serialization/deserialization strategies, register type adapters, and set other parameters.
Strings is a Guava class. You can do your own check if you don't want that dependency.
What I personally don't like in TypeAdapter using answer is the fact you need to describe every field of your entire class which could have lets say 50 fields (which means 50 if blocks in TypeAdapter).
My solution is based on Reflection and a fact Gson will not serialize null values fields by default.
I have a special class which holds data for API to create document called DocumentModel, which has about 50 fields and I don't like to send String fields with "" (empty but not null) values or empty arrays to server. So I created a special method which returns me a copy of my object with all empty fields nulled. Note - by default all arrays in my DocumentModel instance are initialized as empty (zero length) arrays and thus they are never null, you should probably check your arrays for null before checking their length.
public DocumentModel getSerializableCopy() {
Field fields[] = new Field[]{};
try {
// returns the array of Field objects representing the public fields
fields = DocumentModel.class.getDeclaredFields();
} catch (Exception e) {
e.printStackTrace();
}
DocumentModel copy = new DocumentModel();
Object value;
for (Field field : fields) {
try {
value = field.get(this);
if (value instanceof String && TextUtils.isEmpty((String) value)) {
field.set(copy, null);
// note: here array is not being checked for null!
else if (value instanceof Object[] && ((Object[]) value).length == 0) {
field.set(copy, null);
} else
field.set(copy, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return copy;
}
Using this method I don't care if some fields was added or removed after this method was written or whatever. The only problem left - is checking custom type fields, which are not String or array, but this depends to particular class and should be extra coded in if/else blocks.
It seems to me the problem is not with gson. Gson correctly keeps track of the difference between null and an empty string. Are you sure you want to erase that distinction? Are you sure all classes that use TestObject don't care?
What you could do if you don't care about the difference is to change the empty strings to null within a TestObject before serializing it. Or better, make the setters in TestObject such that an empty string is set to null; that way you define rigidly within the class that an empty string is the same as null. You'll have to make sure the values cannot be set outside the setters.
I have ran into the same problem and found 2 distinct solutions
Write a custom TypeAdapter for each field class
TypeAdapter example for String class:
#SuppressWarnings("rawtypes")
public class JSONStringAdapter extends TypeAdapter {
#Override
public String read(JsonReader jsonReader) throws IOException {
String value = jsonReader.nextString();
if(value == null || value.trim().length() == 0) {
return null;
} else {
return value;
}
}
#Override
public void write(JsonWriter jsonWriter, Object object) throws IOException {
String value = String.valueOf(object);
if(value == null || value.trim().length() == 0) {
jsonWriter.nullValue();
} else {
jsonWriter.value(value);
}
}
}
Use:
public class Doggo {
#JsonAdapter(JSONStringAdapter.class)
private String name;
public Doggo(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Doggo aDoggo = new Doggo("");
String jsonString = new Gson().toJson(aDoggo);
}
}
Process the object manually before generating the JSON string
Seems to work on anything, haven't tested the performance:
public static boolean removeEmpty(JSONObject source) {
if (null == source || source.length() == 0) {
return true;
}
boolean isJsonObjectEmpty = false;
for (String key : JSONObject.getNames(source)) {
Object value = source.get(key);
boolean isValueEmpty = isValueEmpty(value);
if(isValueEmpty) {
source.remove(key);
}
}
if(source.length() == 0) {
isJsonObjectEmpty = true;
}
return isJsonObjectEmpty;
}
private static boolean isValueEmpty(Object value) {
if (null == value) {
return true;
}
if (value instanceof JSONArray) {
JSONArray arr = (JSONArray) value;
if(arr.length() > 0) {
List<Integer> indextesToRemove = new ArrayList<>();
for(int i = 0; i< arr.length(); i++) {
boolean isValueEmpty = isValueEmpty(arr.get(i));
if(isValueEmpty) {
indextesToRemove.add(i);
};
}
for(Integer index : indextesToRemove) {
arr.remove(index);
}
if(arr.length() == 0) {
return true;
}
} else {
return true;
}
} else if (value instanceof JSONObject) {
return removeEmpty((JSONObject) value);
} else {
if (JSONObject.NULL.equals(value)
|| null == value
|| value.toString().trim().length() == 0)
) {
return true;
}
}
return false;
}
Use:
public class Doggo {
private String name;
public Doggo(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Doggo aDoggo = new Doggo("");
// if you are not using Type Adapters for your fields
JSONObject aJSONObject1 = new JSONObject(aDoggo);
removeEmpty(aJSONObject1);
String jsonString1 = aJSONObject1.toString();
// if you are using Type Adapters for your fields
Gson gsonParser = new Gson();
JSONObject aJSONObject2 = new JSONObject(gsonParser .toJson(aDoggo));
removeEmpty(aJSONObject2);
String jsonString2 = aJSONObject2.toString();
}
}
Imagine if I have the following JSON
{"game":"football", "people":"elevent"}
{"game":"badminton", "people":"two"}
My class as below
class Sport {
String game;
String people;
}
I could do a deserialize of my Json as below
Sport mySport = Gson().fromJson(json, Sport.class);
However, if my JSON is only
{"game":"football"}
{"game":"badminton"}
I would like it to automatically initialize people to "elevent" or "two", pending of the first field. Is there a way to configure my GsonBuilder() to have that achieve automatically during deserialization?
You could create a custom JsonDeserializer:
public class SportDeserializer implements JsonDeserializer<Sport> {
#Override
public Sport deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = (JsonObject) json;
String game = jsonObject.get("game").getAsString();
JsonElement nullablePeople = jsonObject.get("people");
String people = nullablePeople == null ? null : nullablePeople.getAsString();
if (people == null || people.isEmpty()) {
if (game.equals("football")) {
people = "elevent";
}
else if (game.equals("badminton")) {
people = "two";
}
}
Sport sport = new Sport();
sport.game = game;
sport.people = people;
return sport;
}
}
And then use the custom JsonDeserializer:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Sport.class, new SportDeserializer());
Gson gson = gsonBuilder.create();
Sport sport = gson.fromJson(jsonString, Sport.class);
My answer below isn't the best for this question since I simplified the question, and the other answer would suite this better.
But for more complicated scenarios, my answer below would help. It is basically setup a post-processing after the GSon converted.
I finally use Gson convert post-processing.
class Sport implements PostProcessingEnabler.PostProcessable {
String game;
String people;
#Override
public void gsonPostProcess() {
// The below is something simple.
// Could have more complicated scneario.
if (game.equals("football")) {
people = "elevant";
} else if (game.equals("badminton")) {
people = "two";
}
}
}
class PostProcessingEnabler implements TypeAdapterFactory {
public interface PostProcessable {
void gsonPostProcess();
}
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
T obj = delegate.read(in);
if (obj instanceof PostProcessable) {
((PostProcessable) obj).gsonPostProcess();
}
return obj;
}
};
}
}
Tribute goes to https://blog.simplypatrick.com/til/2016/2016-03-02-post-processing-GSON-deserialization/
I'm using retrofit with gson to deserialize my json into realm objects. This works very well for the most part. Trouble arises when dealing with
RealmList(String(or any other basic data type))
Since Realm doesnt support RealmList where E doesnt extend Realm object, I wrapped String in a RealmObject.
public class RealmString extends RealmObject {
private String val;
public String getValue() {
return val;
}
public void setValue(String value) {
this.val = value;
}
}
My realm Object is as below
public class RealmPerson extends RealmObject {
#PrimaryKey
private String userId;
...
private RealmList<RealmString> stringStuff;
private RealmList<SimpleRealmObj> otherStuff;
<setters and getters>
}
SimpleRealmObj works fine as it only has String elements
public class SimpleRealmObj extends RealmObject {
private String foo;
private String bar;
...
}
How can I deserialize stringStuff? I tried using a gson TypeAdapter
public class RealmPersonAdapter extends TypeAdapter<RealmPerson> {
#Override
public void write(JsonWriter out, RealmPerson value) throws IOException {
out.beginObject();
Log.e("DBG " + value.getLastName(), "");
out.endObject();
}
#Override
public RealmPerson read(JsonReader in) throws IOException {
QLRealmPerson rList = new RealmPerson();
in.beginObject();
while (in.hasNext()) {
Log.e("DBG " + in.nextString(), "");
}
in.endObject();
return rList;
}
However I still hit the IllegalStateException
2334-2334/com.qualcomm.qlearn.app E//PersonService.java:71﹕ main com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was NAME at line 1 column 3 path $.
I tried RealmList, RealmString adapter earlier to no avail.
The only workaround I managed to find so far is https://github.com/realm/realm-java/issues/620#issuecomment-66640786
Any better options?
It is better to use JsonSerializer and JsonDeserializer rather than TypeAdapter for your RealmObject, because of 2 reasons:
They allow you to delegate (de)serialization for your RealmObject to the default Gson (de)serializer, which means you don't need to write the boilerplate yourself.
There's a weird bug in Gson 2.3.1 that might cause a StackOverflowError during deserialization (I tried the TypeAdapter approach myself and encountered this bug).
Here's how (replace Tag with your RealmObject class):
(NOTE that context.serialize and context.deserialize below are equivalent to gson.toJson and gson.fromJson, which means we don't need to parse the Tag class ourselves.)
Parser + serializer for RealmList<Tag>:
public class TagRealmListConverter implements JsonSerializer<RealmList<Tag>>,
JsonDeserializer<RealmList<Tag>> {
#Override
public JsonElement serialize(RealmList<Tag> src, Type typeOfSrc,
JsonSerializationContext context) {
JsonArray ja = new JsonArray();
for (Tag tag : src) {
ja.add(context.serialize(tag));
}
return ja;
}
#Override
public RealmList<Tag> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
RealmList<Tag> tags = new RealmList<>();
JsonArray ja = json.getAsJsonArray();
for (JsonElement je : ja) {
tags.add((Tag) context.deserialize(je, Tag.class));
}
return tags;
}
}
Tag class:
#RealmClass
public class Tag extends RealmObject {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Then register your converter class with Gson:
Gson gson = new GsonBuilder()
.registerTypeAdapter(new TypeToken<RealmList<Tag>>() {}.getType(),
new TagRealmListConverter())
.create();
The error message "Expected a string but was NAME" can be solved by retrieving the name of the json object in the JsonReader before the actual json object (which is a String in your case).
You can take a look at the Android documentation for JsonReader. It has detailed explanation and code snippet. You can also take a look at the readMessage method in the sample code snippet in the documentation.
I have modified your read method to what I think it should be. NOTE: I didn't test the code, so there may be some minor errors in it.
#Override
public RealmPerson read(JsonReader in) throws IOException {
RealmPerson rList = new RealmPerson();
in.beginObject();
String name = "";
while (in.hasNext()) {
name = in.nextName();
if (name.equals("userId")) {
String userId = in.nextString();
// update rList here
} else if (name.equals("otherStuff")) {
// since otherStuff is a RealmList of RealmStrings,
// your json data would be an array
// You would need to loop through the array to retrieve
// the json objects
in.beginArray();
while (in.hasNext()) {
// begin each object in the array
in.beginObject();
name = in.nextName();
// the RealmString object has just one property called "value"
// (according to the code snippet in your question)
if (name.equals("val")) {
String val = in.nextString();
// update rList here
} else {
in.skipValue();
}
in.endObject();
}
in.endArray();
} else {
in.skipValue();
}
}
in.endObject();
return rList;
}
Let me know if this helps.
My gson typeAdapter was the culprit.
The above error was seen as I wasnt deserializing the json into RealmPerson correctly, the first field is not a String, hence
in.nextString()
was borking.
I looked at some example code and it hit me, I didnt have to use
in.beginObject() and in.endObject()
to deserialize a String. The below code works.
public class QLRealmStringAdapter extends TypeAdapter<QLRealmString> {
#Override
public void write(JsonWriter out, QLRealmString value) throws IOException {
Log.e("DBG " + value.getValue(), "");
out.value(value.getValue());
}
#Override
public RealmString read(JsonReader in) throws IOException {
RealmString rString = new RealmString();
if (in.hasNext()) {
String nextStr = in.nextString();
System.out.println("DBG " + nextStr);
rString.setValue(nextStr);
}
return rString;
}
}
Hope this helps someone.
i need a jackson serializer and deserializer for the Converting Arraylist to RealmList
How can I use GSON with mixed type fields, is it possible?
{
'field': false
}
// or
{
'field': [
1,2,3,4
]
}
My GSON class:
public class MyModel {
public HashMap<ArrayList,Boolean> blockedusers;
}
Yes it is possible, but you have to handle if the json is an array or a primitive type. try code below:
String case1 = "{'field':false}";
String case2 = "{'field':[1,2,3,4]}";
JsonElement jsonElement = ((JsonObject)(new JsonParser().parse(case1))).get("field");
if(jsonElement instanceof JsonArray) {
JsonArray jsonArray = (JsonArray)jsonElement;
if(jsonArray != null && jsonArray.size() > 0) {
for (JsonElement aJsonElement : jsonArray) {
// TODO: handle json element inside array
System.out.println(aJsonElement);
}
}
} else if (jsonElement instanceof JsonPrimitive) {
boolean value = jsonElement.getAsBoolean();
System.out.println("value:" + value);
}
Also you can write a custom TypeAdapter. See my answer for another question.
As suggested in GSON parsing unspecified type variable
I think you have to define a custom deserializer:
public class MyJsonDeserializer implements JsonDeserializer<YourParsedData> {
#Override
public YourParsedData deserialize(final JsonElement je, final Type type, final JsonDeserialization Context jdc) throws JsonParseException
{
final JsonObject obj = je.getAsJsonObject(); //our original full json string
final JsonElement serviceElement = obj.get("field");
//here we provide the functionality to handle the naughty element. It seems emtpy string is returned as a JsonPrimitive... so one option
if(serviceElement instanceOf JsonPrimitive)
{
//get Boolean
}
return YourParsedData.create(); //provide the functionality to take in the parsed data
}
}
you can use it with:
Gson gson = new GsonBuilder()
.registerTypeAdapter(ServiceState.class, new ServiceDeserializer())
.create();