How to make Gson read value as String? - gson

My JSON objects look like this
{"phoneNbr":"123456789","firstName":"Mark","previousNames":[{"previous1":"Peter","previous2":"Steve"}]}
{"phoneNbr":"234567891","firstName":"Hank","previousNames":null}
The previousNames values can be anything. I want it to be treated a STRING always. However when I try to parse it, GSON complaints because it expects array.
PersonJsonDAO class looks like this
private String phoneNbr;
private String firstName;
private String previousNames;
I try to parse it but GSON says Expected a string but was BEGIN_ARRAY
PersonJsonDAO personJsonDAO= new Gson().fromJson(jsonString, PersonJsonDAO.class);
How can I force GSON to accept previousNames as String?

GSON is treating it as an array, because it is indeed an array :)
I can think of 4 different alternatives to meet your desired behavior:
A preprocessing step of turning everything after '"previousNames":' into a string, by searching for the first occurance of '"previousNames":[', inserting there a '"', backspacing all the double quotes, till the occurrence of ']', before which I would add another double quote.
a much easier solution, if you don't mind the slight computational overhead, which in your case is probably tiny, just parse into a JSON as a first step, like you did, but declaring previousNames as an array of Strings, and then calling:
personJsonDAO.getString("previousNames");
However, this will leave you with previousNames field as an array of Strings.
Another option is to leave it as a JSonObject in the deserilization process, like this:
class PersonJsonDAO {
....
#SerializedName("previousNames")
JsonObject previousNames;
....
}
If the above alternatives are not enough, and you insist on having the previousNames field as a String, then the most comprehensive and correct approach would be to override the desiarilzation process of GSON, calling super for all behaviours, except when meeting the previousNames culprit, which you would return as a String.

Related

Initialize non existing keys to empty array list

I have a class :
class Con {
private List<Ind> inds;
}
I am using Gson in the usual way to convert a JSON string to this class object. so in case, the JSON doesn't have the key inds present this variable inds is assigned a null value. Is there a way to assign inds an empty ArrayList instead?
My Thoughts:
One straightforward way could be once the Gson object is built. Go over all the null objects and assign them to the new ArrayList<>(). Is there a better approach?
public List<Ind> getInds() {
return inds;
}
Currently I am using the above getter in a code like : con.getInds().stream() which is causing NullPointerException.
I am not sure what would be a good way to resolve this. Instead of List Should I return an Optional or Should I modify this getter like
public List<Ind> getInds() {
inds==null?new ArrayList<>():inds;
}
The above will also resolve the NullPointerException. Not sure if there are pros and cons to using this approach. Although now there is no way to identify if the Json has a key with name inds or not. For the current code that I am writing this may not be required. But there is a meaning loss here certainly.
One solution to this would be to assign default values to the fields, for example:
class Con {
private List<Ind> inds = new ArrayList<>();
}
Gson will keep this default value; only if the field is present in the JSON data it will reassign the field value.
There are however a few things to keep in mind:
Your class needs a no-args constructor (implicit or explicit); otherwise Gson might create instances without invoking the initializer blocks of the class, and therefore the field will be null
If the field is present in JSON but has a JSON null value, then Gson will still set that as value
You cannot tell afterwards whether the field was present in JSON but had an empty JSON array as value, or whether it was missing

Sense of Optional.orElse() [duplicate]

Why does this throw a java.lang.NullPointerException?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
The first item in strings is null, which is a perfectly acceptable value. Furthermore, findFirst() returns an Optional, which makes even more sense for findFirst() to be able to handle nulls.
EDIT: updated the orElse() to be less ambiguous.
The reason for this is the use of Optional<T> in the return. Optional is not allowed to contain null. Essentially, it offers no way of distinguishing situations "it's not there" and "it's there, but it is set to null".
That's why the documentation explicitly prohibits the situation when null is selected in findFirst():
Throws:
NullPointerException - if the element selected is null
As already discussed, the API designers do not assume that the developer wants to treat null values and absent values the same way.
If you still want to do that, you may do it explicitly by applying the sequence
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
to the stream. The result will be an empty optional in both cases, if there is no first element or if the first element is null. So in your case, you may use
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
to get a null value if the first element is either absent or null.
If you want to distinguish between these cases, you may simply omit the flatMap step:
Optional<String> firstString = strings.stream()
.map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
firstString.orElse("first element is null"));
This is not much different to your updated question. You just have to replace "no such element" with "StringWhenListIsEmpty" and "first element is null" with null. But if you don’t like conditionals, you can achieve it also like:
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
Now, firstString will be null if an element exists but is null and it will be "StringWhenListIsEmpty" when no element exists.
You can use java.util.Objects.nonNull to filter the list before find
something like
list.stream().filter(Objects::nonNull).findFirst();
The following code replaces findFirst() with limit(1) and replaces orElse() with reduce():
String firstString = strings.
stream().
limit(1).
reduce("StringWhenListIsEmpty", (first, second) -> second);
limit() allows only 1 element to reach reduce. The BinaryOperator passed to reduce returns that 1 element or else "StringWhenListIsEmpty" if no elements reach the reduce.
The beauty of this solution is that Optional isn't allocated and the BinaryOperator lambda isn't going to allocate anything.
Optional is supposed to be a "value" type. (read the fine print in javadoc:) JVM could even replace all Optional<Foo> with just Foo, removing all boxing and unboxing costs. A null Foo means an empty Optional<Foo>.
It is a possible design to allow Optional with null value, without adding a boolean flag - just add a sentinel object. (could even use this as sentinel; see Throwable.cause)
The decision that Optional cannot wrap null is not based on runtime cost. This was a hugely contended issue and you need to dig the mailing lists. The decision is not convincing to everybody.
In any case, since Optional cannot wrap null value, it pushes us in a corner in cases like findFirst. They must have reasoned that null values are very rare (it was even considered that Stream should bar null values), therefore it is more convenient to throw exception on null values instead of on empty streams.
A workaround is to box null, e.g.
class Box<T>
static Box<T> of(T value){ .. }
Optional<Box<String>> first = stream.map(Box::of).findFirst();
(They say the solution to every OOP problem is to introduce another type :)

Trie with association between words

I have a usecase where i want to correct words.
I have set of correct and incorrect words[spelling mistake].
I populate the trie with all words.
i have both the correct and incorrect version of every word.
Now in case if i get word as "a" for correction ,
-- i search it in trie.if trie has this word , i want to associate this word with the correct version of this word.
Solution :
i can set correct version ["a1"] of word at last node of incorrect word in trie. And can resolve it to "a1" .
But i have to store correct version of each word at last node that will increase the memory foot print .
Since i have all words loaded in to trie [correct/incorrect].
Is there any way i can make association between correct and incorrect word without again storing entire word in last node as value ?
Any pointer ?
public class TrieNode<T> {
private Map<Character, TrieNode<T>> childs;
private boolean complete;
private T value;
....
}
You could use a single dictionary for this. In C#, that would be:
Dictionary<string, string> MisspellingsLookup = new Dictionary<string, int>();
The key is the misspelling, and the value is the correct spelling.
Now, some words are commonly misspelled in multiple ways. For example, "occasion" is often misspelled as "ocassion" or "occassion" If you want to reduce the memory used by the multiple misspellings, you can use a temporary dictionary during construction. Whenever you add a misspelling, you look up the correct spelling in the good words dictionary, and if it's already there then you use that value. So all you do is store a reference to an existing word rather than creating a new string. Here's an example:
Dictionary<string, string> GoodWords = new Dictionary<string, int>();
Dictionary<string, string> Misspellings = new Dictionary<string, string>();
void AddMisspelling(string misspelled, string correct)
{
string goodWord;
if (!GoodWords.TryGetValue(correct, out goodWord))
{
goodWord = correct;
GoodWords.Add(correct, correct);
}
// Always use goodWord here, so you're not creating duplicate strings.
Misspellings.Add(misspelled, goodWord);
}
When you're done adding words, you can clear the GoodWords dictionary to save space:
GoodWords = null;
I recommend a dictionary here because it'll almost certainly use less memory, and lookup is O(1) rather than O(word length).

SuperCSV with null delimiter

I'm creating a file that isn't really a csv file, but SuperCSV can help me to make the creation of this file easier. The structure of the file uses different lengths for each line, following a layout that don't separate the different information. So, to know which information has in one line you need look at the first 2 characters (the name of the register), count the characters and extract it by size.
I've configured SuperCSV to use empty delimiter, however, the created file is using a space where it should have nothing.
public class TarefaGerarArquivoRegistrosFiscais implements ITarefa {
private static final CsvPreference FORMATO_ANEXO_IV = new CsvPreference.Builder('"', '\0' , "\r\n").build();
public void processar() {
try {
writer = new CsvListWriter(getFileWriter(), FORMATO_ANEXO_IV);
writer.write(geradorRegistroU1.gerar());
} finally {
if (writer != null)
writer.close();
}
}
}
I'm doing something wrong? '\0' is the correct code for a null char?
It's probably not what you want to hear, but I wouldn't recommend using Super CSV for this (and I'm a committer!). Its sole purpose is to deal with delimited files - and you're not using delimiters.
You could misuse Super CSV by creating a wrapper object (containing your List) whose toString() method simply concatenates all of the values together, then passing that single object to writer.write(), but it's an awful hack.
I'd recommend either finding another library more suited to your problem, or writing your own solution.

Serializing JsonArray as array and not object using Gson

I'm using Gson library but when it serializes the JsonArray object, it seems to serialize this as an object rather than a JSON array. i.e.
{ elements: [ {name:"value1}, {name:"value2"}]}
How do I remove the elements from being serialized?
I went to see the doctor, because my foot hurt when I walked on it. The doctor said, "Don't walk on it."
Generally, when working with an API like Gson, one would rather not even know that JsonArray exists, and they'd instead just use the data binding part of the API. So, instead of manually building a JsonArray, and then deal with serializing it, just feed a Java List or array to Gson.toJson(). For example:
List list = new ArrayList();
list.add("one");
list.add(2);
list.add(new Foo());
Gson gson = new Gson();
String json = gson.toJson(list);
System.out.println(json);
If that approach doesn't fit your needs and you're stuck using a JsonArray for some reason, then you might be tempted to just call its toString() method, since that does currently create what's ultimately desired, I wouldn't use it, because there is nothing in the documentation that says the toString() is guaranteed to create a valid JSON representation of the enclosed array contents. So, it might not return a String of the same format in future releases of Gson.
At any rate, if you really want to use a JsonArray, it should serialize well enough as follows.
JsonElement one = new JsonPrimitive("one");
JsonElement two = new JsonPrimitive(2);
JsonObject foo = new JsonObject();
foo.addProperty("foo", new Foo().foo);
JsonArray jsonArray = new JsonArray();
jsonArray.add(one);
jsonArray.add(two);
jsonArray.add(foo);
System.out.println(new Gson().toJson(jsonArray));
// ["one",2,{"foo":"hi"}]
Note: This answer is based on the Gson 2.2 API. I don't recall whether earlier versions of Gson included the overloaded toJson(JsonElement) methods.
If the toJson method is already being used in this fashion (to serialize a JsonArray), but the output is as demonstrated in the original question, recall that Java doesn't consider the runtime type when selecting amongst overloaded methods. It binds to the compile time type. (Lame -- I know.) So, you may need to cast the argument type to JsonElement, to let the compiler know which method to bind to. The following demonstrates what might be effectively happening in the original question.
System.out.println(new Gson().toJson((Object)jsonArray));
// {"elements":["one",2,{"foo":"hi"}]}

Resources