I have this bean
class Toto
{
private final String fuu;
private final String bar;
Private List<Dog> dogs;
}
class Dog {
private String name;
private String race;
}
I want to convert this bean into a Map<> using:
ObjectMapper serializer = new ObjectMapper();
serializer.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return serializer.convertValue(entry, Map.class);
This code result is that Dog objects are also converted into a Map.
I want a solution so that in the final map i would have:
fuu=fuu_value_as_String
bar=bar_value_as_String
dogs=[ObjectDog1, ObjectDog2] // i do not want the Object to be
converted (because i have an instanceof check later in the code)
Thanks
Related
I have a class :
data class Stam(#SerializedName("blabla") val blabla: String = "")
I want to do gson.fromJson("{\"blabla\":null}", Stam::class.java)
However, it will fail because blabla is not nullable.
I want to make it so if gson failed to deserialize some variable, it will take the default value I give it.
How to achieve that?
I don't think it is possible with GSON, this is one of the reasons why kotlinx.serialization library was created. With this library it is fairly easy:
#Serializable
data class Stam(#SerialName("blabla") val blabla: String = "") //actually, #SerialName may be omitted if it is equal to field name
Json { coerceInputValues = true }.decodeFromString<Stam>("{\"blabla\":null}")
I wouldn't say it is not possible in Gson, but Gson is definitely not the best choice:
Gson has no mention on Kotlin, its runtime and specifics, so one is better to use a more convenient and Kotlin-aware tool. Typical questions here are: how to detect a data class (if it really matters, can be easily done in Kotlin), how to detect non-null parameters and fields in runtime, etc.
Data classes in Kotlin seem to provide a default constructor resolvable by Gson therefore Gson can invoke it (despite it can instantiate classes instances without constructors using unsafe mechanics) delegating to the "full-featured" constructor with the default arguments. The trick here is removing null-valued properties from input JSON so Gson would keep "default-argumented" fields unaffected.
I do Java but I do believe the following code can be converted easily (if you believe Gson is still a right choice):
final class StripNullTypeAdapterFactory
implements TypeAdapterFactory {
// The rule to check whether this type adapter should be applied.
// Externalizing the rule makes it much more flexible.
private final Predicate<? super TypeToken<?>> isClassSupported;
private StripNullTypeAdapterFactory(final Predicate<? super TypeToken<?>> isClassSupported) {
this.isClassSupported = isClassSupported;
}
static TypeAdapterFactory create(final Predicate<? super TypeToken<?>> isClassSupported) {
return new StripNullTypeAdapterFactory(isClassSupported);
}
#Override
#Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !isClassSupported.test(typeToken) ) {
return null;
}
// If the type is supported by the rule, get the type "real" delegate
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new StripNullTypeAdapter<>(delegate);
}
private static final class StripNullTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegate;
private StripNullTypeAdapter(final TypeAdapter<T> delegate) {
this.delegate = delegate;
}
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegate.write(out, value);
}
#Override
public T read(final JsonReader in) {
// Another disadvantage in using Gson:
// the null-stripped object must be buffered into memory regardless how big it is.
// So it may generate really big memory footprints.
final JsonObject buffer = JsonParser.parseReader(in).getAsJsonObject();
// Strip null properties from the object
for ( final Iterator<Map.Entry<String, JsonElement>> i = buffer.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<String, JsonElement> property = i.next();
if ( property.getValue().isJsonNull() ) {
i.remove();
}
}
// Now there is no null values so Gson would only use properties appearing in the buffer
return delegate.fromJsonTree(buffer);
}
}
}
Test:
public final class StripNullTypeAdapterFactoryTest {
private static final Collection<Class<?>> supportedClasses = ImmutableSet.of(Stam.class);
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
// I don't know how easy detecting data classes and non-null parameters is
// but since the rule is externalized, let's just lookup it
// in the "known classes" registry
.registerTypeAdapterFactory(StripNullTypeAdapterFactory.create(typeToken -> supportedClasses.contains(typeToken.getRawType())))
.create();
#Test
public void test() {
final Stam stam = gson.fromJson("{\"blabla\":null}", Stam.class);
// The test is "green" since
Assertions.assertEquals("", stam.getBlabla());
}
}
I still think Gson is not the best choice here.
It has been already clarified what's the difference between val and const val here.
But my question is, why we should use const keyword? There is no difference from the generated Java code perspective.
This Kotlin code:
class Application
private val testVal = "example"
private const val testConst = "another example"
Generates:
public final class ApplicationKt
{
private static final String testVal = "example";
private static final String testConst = "another example";
}
It's not always the same generated code.
If testVal and testConst were public, the generated code wouldn't be the same. testVal would be private with a public get, whereas testConst would be public, without any getter. So const avoids generating a getter.
In my opinion the main difference is that val means that no setter will be generated for the property (but a getter will be generated) and not that the value is constant, while a const val is a constant (like a Java's private/public static final xxx).
Example:
class Foo {
private val testVal: String
get() = Random().nextInt().toString()
}
As directly mentioned in the documentation, testConst can be used in annotation parameters, but testVal can't.
More generally speaking, const guarantees that you have a constant variable in the Java sense, and
Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1), reachability (§14.21), and definite assignment (§16.1.1).
You don't see the difference between generated code because your variables are private. Otherwise the result would have the getter for testVal:
public final class ApplicationKt {
#NotNull
private static final String testVal = "example";
#NotNull
public static final String testConst = "another example";
#NotNull
public static final String getTestVal() {
return testVal;
}
}
So in your particular case it is the same, except you can use const properties in annotations:
const val testVal: String = "This subsystem is deprecated"
#Deprecated(testVal) fun foo() { ... }
There are also differences in using them.
Example of constants(Kotlin):
class Constants {
companion object {
val EXAMPLE1 = "example1" // need companion and a getter
const val EXAMPLE2 = "example2" // no getter, but companion is generated and useless
#JvmField val EXAMPLE3 = "example3"; // public static final with no getters and companion
}
}
How to use(Java):
public class UseConstants {
public void method(){
String ex1 = Constants.Companion.getEXAMPLE1();
String ex2 = Constants.EXAMPLE2;
String ex3 = Constants.EXAMPLE3;
}
}
"Consts" are compile time Constants whereas "val" is used to define constants at run time.
This means, that "consts" can never be assigned to a function or any class constructor, but only to a String or primitive.
Example:
const val NAME = "M Sakamoto"
val PICon = getPI()
fun getPI(): Double {
return 3.14
}
fun main(args: Array<String>) {
println("Name : $NAME")
println("Value of PI : $PICon")
}
Output:
Name : M Sakamoto
Value of PI : 3.14
I have a simple class used for JSON serialization. For this purpose, the external interface uses Strings, but the internal representation is different.
public class TheClass {
private final ComplexInfo info;
public TheClass(String info) {
this.info = new ComplexInfo(info);
}
public String getInfo() {
return this.info.getAsString();
}
// ...more stuff which uses the ComplexInfo...
}
I have this working in Kotlin (not sure if there's a better way). But the non-val/var constructor prevents me from using data.
/*data*/ class TheClass(info: String) {
private val _info = ComplexInfo(info)
val info: String
get() = _info.getAsString()
// ...more stuff which uses the ComplexInfo...
}
How do I get this working as a data class?
You can use a combination of a private ComplexInfo property declared in the primary constructor and a secondary constructor that accepts a String.
Optionally, make the primary constructor private.
Example:
data class TheClass private constructor(private val complexInfo: ComplexInfo) {
constructor(infoString: String) : this(ComplexInfo(infoString))
val info: String get() = complexInfo.getAsString()
}
Note that it's the complexInfo property that is used in the data class generated members implementations.
I am new in kotlin and not able to understand how the getter and setter behave in kotlin, so if I set the setter to private. Then what is the way of updating the value.
package foo
class Person() {
var name: String = "defaultValue"
private set
}
If you set your setter to be private, then this setter will be accessible only from within its class. In other words you can use normal assignment even when your setter is private but only from within the class.
class Person() {
var name: String = "defaultValue"
private set
fun foo(bar: String) {
name = bar // name can be set here
}
}
fun main(args: Array<String>) {
Person().name = "foo" // error. Name can be accessed but can not be modified here as its setter is private.
}
For more information check the Kotlin's Visibility documentation.
the kotlin code above will be transform to java code by kotlin compiler more like as below:
package foo;
public final class Person{
private String name = "defaultValue";
public final String getName(){
return name;
}
private final void setName(String name){
this.name=name;
}
}
which means you can only change the name field in the Person class. another situation is if you want to modify the name property with private setter out of the Person class. you can using java reflection instead, for example:
val person = Person();
val field = Person::name.javaField!!.apply { isAccessible = true }
field.set(person, "bob")
println(person.name)// "bob"
val setter = Person::class.java.getDeclaredMethod("setName", String::class.java)!!
.apply {
isAccessible = true
}
setter.invoke(person, "john")
println(person.name)// "john"
public class ListResult<T> {
private boolean ok;
private String message;
private java.util.List<T> data;
private Paging paging;
}
When i want to deserialize JSON given form RESTful call like this :
Type fooType = new TypeToken<ListResult<T>>() {}.getType();
Gson gson = new Gson();
Object model = gson.fromJson(strResult, fooType);
A get A ListResult where data field is a list of StringMap instead a List of T like defined in ListResult class
Any idea ?
Type fooType = new TypeToken<ListResult<StringMap>>() {}.getType();
Gson gson = new Gson();
ListResult<StringMap> model = gson.fromJson(strResult, fooType);
Gson can't read your mind, or determine a Generic type all on its own ;)
https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Generic-Types
Just don't use a list, use an array MyObj[] myObjs. You don't need to use the TypeToken way