How to ignoring empty string during deserialization by GSON - java

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);
}

Related

Keep part of JSON as String with JacksonDeserializer

I want to keep a part of a JSON as String value.
As far as i know, there is no way with Annotations, but i could not find a way how to get the full Object/Array value as String.
There is a Workaround, which works, by reading it as an Object and instantly write it back as an String by using the ObjectMapper of Jackson.
You can imagine, this is a horrible solution for very big JSONs.
public class DeserializeTest {
private static ObjectMapper mapper;
public static void main(String[] args) throws IOException {
mapper = Jackson2ObjectMapperBuilder.json().build();
mapper.findAndRegisterModules();
SimpleModule module = new SimpleModule();
module.addDeserializer(TestClassWrapper.class, new TestDeserializer());
mapper.registerModule(module);
String json = "{\"name\":\"testprop\", \"data\":[{\"prop\":\"test\"},{\"prop\":\"test1\"},{\"prop\":\"test2\"}]}";
TestClassWrapper t = mapper.readValue(json, TestClassWrapper.class);
// later in program, when i know the expected class
TestClass o = unwrap(t, new TypeReference<ArrayList<Test2>>() {});
}
public static class TestClassWrapper {
String name;
String data;
// removed getter and setter
}
public static class TestClass {
String name;
List<Test2> data;
// removed getter and setter
}
public static class Test2 {
String prop;
// removed getter and setter
}
public static class TestDeserializer extends JsonDeserializer<TestClassWrapper> {
#Override
public TestClassWrapper deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
TestClassWrapper t = new TestClassWrapper();
String key = p.getCurrentName();
if (key == null) {
p.nextToken();
key = p.getCurrentName();
}
for (; key != null; key = p.nextFieldName()) {
p.nextToken();
switch (key) {
case "name":
t.name = p.getValueAsString();
break;
case "data":
// what i tried:
System.out.println(p.getText()); // [
System.out.println(p.getValueAsString()); // NULL
System.out.println(p.getCurrentValue()); //NULL
System.out.println(p.getCurrentToken()); // [ TOKEN
System.out.println(p.getParsingContext().getCurrentValue()); // NULL
System.out.println(p.getParsingContext().toString()); // [0]
System.out.println(p.getEmbeddedObject()); // NULL
System.out.println(p.getTextCharacters()); // [
try {
System.out.println(ctxt.readValue(p, String.class)); // MismatchedInputException
} catch (MismatchedInputException e){}
// The only way i could make it work.
// Parse to a object and write it back as string.
StringBuilder sb = new StringBuilder();
Iterator<Object> it = p.readValuesAs(Object.class);
while (it.hasNext()) {
sb.append(mapper.writeValueAsString(it.next()));
sb.append(it.hasNext() ? "," : "");
}
t.data = p.getCurrentToken() == JsonToken.END_ARRAY ? "[" + sb.toString() + "]" : sb.toString();
break;
}
}
return t;
}
}
public static TestClass unwrap(TestClassWrapper t, TypeReference targetClass) throws IOException {
TestClass o = new TestClass();
o.name = t.name;
o.data = mapper.readValue(t.data, targetClass);
return o;
}
}
How can i tell the JsonParser object, to just give me the String of the current value?
(For data this would be: "[{"prop":"test"}, {"prop":"test1"}, {"prop":"test2"}]")

How to generate Json with Java

I am working on an application where i have to generate a json like this:
[
{"title":"Culture","start":"Salary","end":"Work"},
{"title":"Work","start":"Salary","end":"Work"}
]
But my code generates json like this:
{{"name":"Culture"},[{"name":"Salary"},{"name":"Work"}],}
My code:
public class ParseJson {
public static class EntryListContainer {
public List<Entry> children = new ArrayList<Entry>();
public Entry name;
}
public static class Entry {
private String name;
public Entry(String name) {
this.name = name;
}
}
public static void main(String[] args) {
EntryListContainer elc1 = new EntryListContainer();
elc1.name = new Entry("Culture");
elc1.children.add(new Entry("Salary"));
elc1.children.add(new Entry("Work"));
ArrayList<EntryListContainer> al = new ArrayList<EntryListContainer>();
Gson g = new Gson();
al.add(elc1);
StringBuilder sb = new StringBuilder("{");
for (EntryListContainer elc : al) {
sb.append(g.toJson(elc.name));
sb.append(",");
sb.append(g.toJson(elc.children));
sb.append(",");
}
String partialJson = sb.toString();
if (al.size() > 1) {
int c = partialJson.lastIndexOf(",");
partialJson = partialJson.substring(0, c);
}
String finalJson = partialJson + "}";
System.out.println(finalJson);
}
}
Can anyone help me to generate this json in my required format ?? please thanks in advance
Try this
public class Entry {
public String title;
public String start;
public String end;
}
And in another part of your code
private ArrayList<Entry> entries = new ArrayList<>();
// Fill the entries...
String the_json = new Gson().toJson(entries);
1) First Create your POJO
public class MyJSONObject {
private String title;
private String start;
private String end;
//getter and setter methods
[...]
#Override
public String toString() {
}
}
2) Use com.google.code.gson library
public static void main(String[] args) {
{
ArrayList<MyJSONObject> myJSONArray = new ArrayList<>();
MyJSONObject obj = new MyJSONObject();
obj.setTitle="Culture";
obj.set[...]
myJSONArray.add(obj);
Gson gson = new Gson();
// convert java object to JSON format,
// and returned as JSON formatted string
String json = gson.toJson(myJSONArray);
System.out.println(json);
}
Output : [{"title":"Culture","start":"Salary","end":"Work"}, ...]
I recommend you to use some JSON Java API, like Gson. It's very simple to generate a string json from a POJO object or to create a POJO object from a string json.
The code for generating a string json from a POJO object is like this:
Gson gson = new Gson();
String stringJson = gson.toJson(somePojoObject);
The code for creating a POJO object from a string json is like this:
Gson gson = new Gson();
SomePojoClass object = gson.fromJson(stringJson, SomePojoClass.class);
Note that you can not serialize objects with circular references. This causes infinite recursion.

How to extract array values within nested JSON data

I have JSON data in the following format:
{
"data": {
"id": 14810798216415,
"name": "crescentbahuman.com",
"is_organization": true,
"email_domains": [
"crescentbahuman.com"
]
}
}
I want to get the string in the "email_domains" field. I wrote the following code as my attempt:
JSONObject dataObject2= (JSONObject)jsonObject2.get("data");
long id = (long) dataObject2.get("id");
System.out.println("worksapce id is: " + id);
String name = (String) dataObject2.get("name");
System.out.println("The worksapce name is: " + name);
boolean is_organization = (boolean) dataObject2.get("is_organization");
System.out.println("The workspace is organization: " + is_organization);
JSONArray email_domains = (JSONArray) jsonObject2.get("email_domains");
Iterator<String> iterator = email_domains.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
In this code to get "email_domains" only, a JSON Array object is created that get data from the JSON object and then its iterator is used to get values from within the array. However, it throws a NullPointerException on this line:
Iterator<String> iterator = email_domains.iterator();
I am stuck due to this problem. Can anyone kindly suggest a solution?
If you are using the JSON library from http://www.json.org/java/, then you should not be using JSONObject.get() so frequently. The library has other methods to get specific types, such as getLong(), getJSONArray(), and so forth. For your case with the "email_domains" field, you should try:
JSONArray array = dataObject2.getJSONArray("email_domains");
String value = array.getString(0);
org.json.JSONArray email_domains = (org.json.JSONArray) json.get("email_domains");
int length = email_domains.length();
for(int i = length-1; i > 0; i--) {
org.json.JSONObject jsonData = (org.json.JSONObject) email_domains.get(i);
System.out.println(jsonData);
}
My solution? I hate to be someone to offer a solution in another library... but look into google collections and the Gson helper. It can turn your Json into a map for you, and then back to json again when you are done.
Map map = gson.fromJson(jsonText, Map.class);
JsonArray's can then be cast into List's
try this to fetch "email_domains"
JSONArray email_domains = ((JSONArray) jsonObject).get("email_domains");
or
JSONObject obj = new JSONObject(jsonObject.Tostring());
JSONArray email_domains = obj.optJSONArray("email_domains");
"email_address" is JSONArray so we need to fetch this like
JSONArray email_domains = (JSONArray) dataObject2.getJSONArray("email_domains");
email_domains.get(0); // this will return crescentbahuman.com
Use this implementation
import java.lang.reflect.Field;
import java.util.List;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonConvertor {
private static GsonBuilder gsonBuilder;
private static Gson gson;
private JsonConvertor() {
}
public static Object fromJson(String json, Class clz)
{
gson=new Gson();
return gson.fromJson(json,clz);
}
public static String toJson(Object obj) {
gsonBuilder = new GsonBuilder();
gsonBuilder = gsonBuilder
.addSerializationExclusionStrategy(new CustomIclusionStrategy(
obj.getClass()));
gson = gsonBuilder.create();
String json = gson.toJson(obj);
return json;
}
}
class CustomIclusionStrategy implements ExclusionStrategy {
private Class classToIclude;
private Field[] declaredFields;
private List<FieldAttributes> fields;
public CustomIclusionStrategy(List<FieldAttributes> fields) {
this.fields = fields;
}
public CustomIclusionStrategy(Class classToIclude) {
this.classToIclude = classToIclude;
this.declaredFields=classToIclude.getDeclaredFields();
}
// called only if shouldSkipClass returns false
#Override
public boolean shouldSkipField(FieldAttributes f) {
try {
classToIclude.getSuperclass().getDeclaredField(f.getName());
System.out.println(f.getName());
return true;
} catch (Exception e) {
}
return false;
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
// if returns false shouldSkipField will be called, otherwise
//shouldSkipField will not be called
return false;
}
}
public class Org {
private Data data;
public Org(Data data) {
super();
this.data = data;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public String toJson()
{
return JsonConvertor.toJson(this);
}
public static void main(String[] args) {
String json="{\"data\": {\"id\":\"1\",\"name\":\"org1\",\"is_organization\":true,\"email_domains\": [\"email1\",\"email2\",\"email3\",\"email4\"]}}";
Org o=(Org) JsonConvertor.fromJson(json, Org.class);
System.out.println(o.getData().getEmail_domains());
}
}

Gson deserialization of List<String> into realmList<RealmString>

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

Gson to parse generic type, when type is known from json

I have jsons that contain a message of any type and the json contains a String that says which type the message has.
I want to deserialize them and get 1. an Instance of the messag type representing the message and 2. an instance of Topic where T is the message type.
As examples:
input1
{
"messageType":"String",
"message": "a string"
}
I expect a result after the deserialization to be the same as this done by hand:
Topic<String> t = new Topic<String>(String.class);
String message = "a string";
input2
{
"messageType":"Integer",
"message": 1
}
I expect a result after the deserialization to be the same as this done by hand:
Topic<Integer> t = new Topic<Integer>(Integer.class);
Integer message = 1;
input3
{
"messageType":"MyClass",
"message": {"a": "something", "b": 1}
}
I expect a result after the deserialization to be the same as this done by hand:
Topic<MyClass> t = new Topic<MyClass>(MyClass.class);
MyClass message = new MyClass("something", 1);
input4
... same with other types ...
I think you got the point. But now I need to do this somehow in a generic/abstract way. I tried this, but this will not work:
private enum MessageType {
STRING(String.class), INTEGER(Integer.class), BOOLEAN(Boolean.class), MYCLASS(MyClass.class);
private Class<?> clazz;
MessageType(Class<?> clazz) {
this.clazz = clazz;
}
}
private static class MyJson {
String topicId;
String messageType;
Object message;
}
MyJson<?> myJson = gson.fromJson(input, MyJson.class);
MessageType type = MessageType.valueOf(myJson.messageType);
Class<?> clazz = type.getClass();
??? message = clazz.newInstance(message);
Topic<???> t = new ?????
I don't know what to do?! I need Topic and Message typed, but how??? The following seems so bad:
#SuppressWarnings("unchecked")
private <T> Topic<T> createTopic(Class<T> typeClass) {
try {
return Topic.class.getConstructor(typeClass).newInstance(typeClass);
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Fail");
}
}
That works for me now:
class Topic<MessageType> {
// ...
public Class<MessageType> getMessageTypeClass() // ...
// ...
}
parse result data struct:
public class TopicLine {
public Topic<?> topic;
public Object message;
}
deserializer:
public class MessageDeserializer implements JsonDeserializer<TopicLine> {
private static class InternalParseLine {
String messageType;
JsonElement message;
}
private Map<String, Topic<?>> messageTypes = new HashMap<String, Topic<?>>();
public MessageDeserializer() {
messageTypes.put("Integer", new Topic<Integer>(Integer.class));
// other topics
}
private static class InternalParseLine {
String topicId;
JsonElement message;
}
#Override
public TopicLine deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
InternalParseLine line = context.deserialize(json, InternalParseLine.class);
TopicLine topicLine = new TopicLine();
topicLine.topic = topics.get(line.messageType);
topicLine.message = context.deserialize(line.message, topicLine.topic.getMessageTypeClass());
return topicLine;
}
}
usage:
json = new GsonBuilder()
.registerTypeAdapter(TopicLine.class, new TopicLineDeserializer())
.create();
TopicLine t = json.fromJson(line, TopicLine.class);
In fact, to make it more dynamic I use a topics-manager which I pass to the deserializer in its constructor instead of a static map. So I can register and unregister topic/message-types on the fly during runtime.

Categories

Resources