Add strong typing to objects from JsonSlurper - groovy

I'm having some trouble getting typing to work with the JsonSlurper in Groovy. I'm fairly new to Groovy, and even newer to adding strong types to it - bear with me.
Right now I've created a trait which defines the general shape of my JSON object, and I'm trying to cast the results of parseText to it.
import groovy.json.JsonSlurper
trait Person {
String firstname
String lastname
}
def person = (Person)(new JsonSlurper().parseText('{"firstname": "Lando", "lastname": "Calrissian"}'))
println person.lastname
This throws
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '{firstname=Lando, lastname=Calrissian}' with class 'org.apache.groovy.json.internal.LazyMap' to class 'Person' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: Person(org.apache.groovy.json.internal.LazyMap)
...
I can see why my code doesn't make sense, I'm not trying to change the type of the data (casting), I'm just trying to let my IDE know that this is what's inside of my object.
Is it possible to at least add code completion to my JSON objects? I'd love to get runtime type checking, as well, but it's not necessary.

you could try to use delegate
this allows to wrap class around map
import groovy.json.JsonSlurper
class Person {
#Delegate Map delegate
String getFirstname(){ delegate.get('firstname') }
String getLastname(){ delegate.get('lastname') }
}
def person = new Person(delegate:new JsonSlurper().parseText('{"firstname": "Lando", "lastname": "Calrissian"}'))
println person.lastname
or for example use Gson for parsing:
#Grab(group='com.google.code.gson', module='gson', version='2.8.5')
import com.google.gson.Gson
class Person {
String firstname
String lastname
}
def person = new Gson().fromJson('{"firstname": "Lando", "lastname": "Calrissian"}', Person.class)
assert person instanceof Person
println person.lastname

This actually is a cast and Groovy will try to turn your Map into said object.
From the docs:
The coercion operator (as) is a variant of casting. Coercion converts object from one type to another without them being compatible for assignment.
The way this works for a POJO is to construct a new object using the Map-c'tor. This will either unroll into calling setters or works directly with static compilation.
Be aware, that using maps with excess keys will lead to errors. So I'd only use this for toy projects. Use a proper JSON-mapper like e.g. Jackson instead.
So the solution here is to not use a trait (which is basically a interface) but a regular class.

Related

Is there a substitute for enum in kotlin Android besides enumerated annotation?

So after I created lots of enum classes in android (Kotlin), I learnt that enums are not very space efficient in android. So I tried to find another way of doing it. I found the enumerated annotation - #StringDef. However, Kotlin doesn't seem to support this well and there are no warning or error messages even if I pass something unexpected to a method.
So to clarify what I want to do: I have tons of constant strings that can be classified to different groups (I listed them in different enum class), and when calling some setter functions, I want the caller to choose only from the specific group of things that can be chosen.
For example:
enum class Cat (val breed: String){
AMER_SHORTHAIR("American Shorthair"),
SIAMESE("Siamese");
}
enum class Dog (val breed: String){
GOLDEN_R("Golden Retriever"),
POODLE("Poodle");
}
fun adopt(cat: Cat, dog: Dog){
print("I adopted a "+cat.breed+" and a "+dog.breed)
}
In this case, I can only choose from cats for the first param, and dogs for the second. Is there a way of doing this kind of type-safe methods without using enums?
To avoid using enums, I might need to change the above functionality to:
const val AMER_SHORTHAIR = "American Shorthair"
const val SIAMESE = "Siamese"
const val GOLDEN_R = "Golden Retriever"
const val POODLE = "Poodle"
fun adopt(cat: String, dog: String){...}
This is not ideal since we can get all kinds of typos which happens in our current implementation and is why I switched to enum in the first place. But overall, space consumption > type safe. So if there is no better way, I will need to switch back.
Please let me know if there is any efficient ways to achieve. I've thought about using maps or lists, but indexing or accessing the strings become cumbersome because I need to map the string to themselves (no hard coded strings here except for the first assignment like AMER_SHORTHAIR = "American Shorthair").
Thanks.
I agree with #EpicPandaForce comment: you should not optimize Enums in this way.
That said, there will be a new feature: Inline Classes coming in Kotlin 1.3 (https://blog.jetbrains.com/kotlin/2018/07/see-whats-coming-in-kotlin-1-3-m1/).
With this new feature you could do the following:
inline data class Cat(private val breed: String)
inline data class Dog(private val breed: String)
val AMER_SHORTHAIR = Cat("American Shorthair");
val SIAMESE = Cat("Siamese");
val GOLDEN_R = Dog("Golden Retriever");
val POODLE = Dog("Poodle");
fun adopt(cat: Cat, dog: Dog){
print("I adopted a "+cat.breed+" and a "+dog.breed)
}
Whenever you use this values the compiler inlines to raw strings.
In contrast to type aliases, the compiler is able to check the origin types for inline classes.
I like to use Sealed class instead of Enums; It provides some extra goodies over the normal class; More on this here
Something like this;
class LoggingBehaviour #JvmOverloads constructor(private val logLevel: LogLevel = Debug) {
fun log(tag: String, message: String) {
when (logLevel) {
is Error -> Log.e(tag, message)
is Warn -> Log.w(tag, message)
is Info -> Log.i(tag, message)
is Debug -> Log.d(tag, message)
is Verbose -> Log.v(tag, message)
}
}
}
sealed class LogLevel
object Error : LogLevel()
object Warn : LogLevel()
object Info : LogLevel()
object Debug : LogLevel()
object Verbose : LogLevel()
Also annotations like IntDef and StringDef can be used. More of it here

In which sequence does method call work in groovy?

I am using groovy 2.3.8
I am trying to figure out how method calls work in groovy. Specifically if we have a Java class hierarchy each having a metaClass like below
class A {
}
A.metaClass.hello = {
"hello superclass"
}
class B extends A {
}
B.metaClass.hello = {
"hello subclass"
}
If I use new B().hello() I get hello subclass. If I remove meta class of B then I get hello superclass.
Based on changing the above example I think groovy goes in the below sequence to find which method to call
method-in-subclass's-metaclass ?: subclass-metho ?: method-in-superclass's metaclass ?: method-in-superclass
So how does groovy lookup which method to call?
Well, the hierarchy is the expected object oriented programming method overloading, which is what you witnessed. What differs is the dispatching. Instead of starting with a method lookup in instance's class, it begins with the MOP (meta object protocol).
In layman's terms, because the MOP is programmable, so is the way methods are invoked :)
How it works
The following diagram from Groovy's documentation shows how methods are looked up.
What's not clear in the diagram is that there's an instance metaclass as well, and it comes before the class's metaclass.
Something that may help is looking at an object's or class's .metaClass.methods Methods added through inheritance, traits, metaclass, etc are listed in a flat list. The inheritance hierarchy is flattened. .metaClass.metaMethods on the other hand seems to contain methods added via the GDK. From the list I could not tell method precedence :(
Based on observation, the rule seems to be this: the last MetaClass standing wins.
class A { }
class B extends A { }
A.metaClass.hello = {
"hello superclass"
}
B.metaClass.hello = {
"hello subclass"
}
def b = new B()
assert b.hello() == "hello subclass"
b.metaClass = A.metaClass
assert b.hello() == "hello superclass"

Trivial deserialization failing with YamlDotNet

What can possible go wrong with this:
public void Main()
{
var input = new StringReader(Document);
var deserializer = new Deserializer(namingConvention: new CamelCaseNamingConvention());
var p = deserializer.Deserialize<Person>(input);
Console.WriteLine(p.Name);
}
public class Person
{
public string Name {get;set;}
}
private const string Document = #"Name: Peter";
A serialization exception is thrown:
Property 'Name' not found on type 'YamlDotNet.Samples.DeserializeObjectGraph+Person'
The same happens if I first serialize a Person object using the Serializer.
While the online sample for deserialization works just fine - this trivial code does not. What am I missing? It must be a stupid little detail. (But it happened before with other data structures I tried.)
As it seems, the problem is with the namingConvention parameter. If I don't set it to an instance of CamelCaseNamingConvention all is fine.
Unfortunately the "canonical" example (https://dotnetfiddle.net/HD2JXM) uses it and thus suggests it is important.
For any reason the CamelCaseNamingConvention converts the fields to lowercase in the class (ie. 'Name' to 'name'). As the string is 'Name' and not 'name' the deserialization fails. The example uses lower-case therefore it works....
I had the same problem....

XmlSerializer, XmlArray with dynamic content... how?

To start: This is also for REST deserialiaztion, so a custom XmlSerializer is out of the question.
I have a hjierarchy of classes that need to be serializable and deserializable from an "Envelope". It has an arrayelement named "Items" that can contain subclasses of the abstract "Item".
[XmlArray("Items")]
public Item [] Items { get; set; }
Now I need to add XmlArrayItem, but the number is not "fixed". We use so far reflection to find all subclasses with a KnownTypeProvider so it is easy to extend the assembly with new subtypes. I dont really want to hardcode all items here.
The class is defined accordingly:
[XmlRoot]
[KnownType("GetKnownTypes")]
public class Envelope {
but it does not help.
Changing Items to:
[XmlArray("Items")]
[XmlArrayItem(typeof(Item))]
public Item [] Items { get; set; }
results in:
{"The type
xxx.Adjustment
was not expected. Use the XmlInclude
or SoapInclude attribute to specify
types that are not known statically."}
when tyrying to serialize.
Anyone an idea how I can use XmlInclude to point to a known type provider?
The KnownTypesAttribute does not work for XmlSerializer. It's only used by a DataContractSerializer. I'm quite sure that you can exchange the serializer in WCF, because I have done that for the DataContractSerializer. But if that's not an option, you have to implement IXmlSerializable yourself and handle type lookup there.
Before disqualifying this solution: You just have to implement IXmlSerializable just for a special class which replaces Item[]. Everything else can be handled by the default serializer.
According to: http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/83181d16-a048-44e5-b675-a0e8ef82f5b7/
you can use different XmlSerializer constructor:
new XmlSerializer(typeof(Base), new Type[] { typeof(Derived1), ..});
Instead of enumerating all derived classes in the base definition like this:
[System.Xml.Serialization.XmlInclude(typeof(Derived1))]
[System.Xml.Serialization.XmlInclude(typeof(Derived2))]
[System.Xml.Serialization.XmlInclude(typeof(DerivedN))]
I think you should be able to use your KnownTypeProvider to fill the array in the XmlSerializer's constructor.

how to retrieve nested properties in groovy

I'm wondering what is the best way to retrieve nested properties in Groovy, taking a given Object and arbitrary "property" String. I would like to something like this:
someGroovyObject.getProperty("property1.property2")
I've had a hard time finding an example of others wanting to do this, so maybe I'm not understanding some basic Groovy concept. It seems like there must be some elegant way to do this.
As reference, there is a feature in Wicket that is exactly what I'm looking for, called the PropertyResolver:
http://wicket.apache.org/apidocs/1.4/org/apache/wicket/util/lang/PropertyResolver.html
Any hints would be appreciated!
I don't know if Groovy has a built-in way to do this, but here are 2 solutions. Run this code in the Groovy Console to test it.
def getProperty(object, String property) {
property.tokenize('.').inject object, {obj, prop ->
obj[prop]
}
}
// Define some classes to use in the test
class Name {
String first
String second
}
class Person {
Name name
}
// Create an object to use in the test
Person person = new Person(name: new Name(first: 'Joe', second: 'Bloggs'))
// Run the test
assert 'Joe' == getProperty(person, 'name.first')
/////////////////////////////////////////
// Alternative Implementation
/////////////////////////////////////////
def evalProperty(object, String property) {
Eval.x(object, 'x.' + property)
}
// Test the alternative implementation
assert 'Bloggs' == evalProperty(person, 'name.second')
Groovy Beans let you access fields directly. You do not have to define getter/setter methods. They get generated for you. Whenever you access a bean property the getter/setter method is called internally. You can bypass this behavior by using the .# operator. See the following example:
class Person {
String name
Address address
List<Account> accounts = []
}
class Address {
String street
Integer zip
}
class Account {
String bankName
Long balance
}
def person = new Person(name: 'Richardson Heights', address: new Address(street: 'Baker Street', zip: 22222))
person.accounts << new Account(bankName: 'BOA', balance: 450)
person.accounts << new Account(bankName: 'CitiBank', balance: 300)
If you are not dealing with collections you can simply just call the field you want to access.
assert 'Richardson Heights' == person.name
assert 'Baker Street' == person.address.street
assert 22222 == person.address.zip
If you want to access a field within a collection you have to select the element:
assert 'BOA' == person.accounts[0].bankName
assert 300 == person.accounts[1].balance​​​​​​​​​
You can also use propertyMissing. This is what you might call Groovy's built-in method.
Declare this in your class:
def propertyMissing(String name) {
if (name.contains(".")) {
def (String propertyname, String subproperty) = name.tokenize(".")
if (this.hasProperty(propertyname) && this."$propertyname".hasProperty(subproperty)) {
return this."$propertyname"."$subproperty"
}
}
}
Then refer to your properties as desired:
def properties = "property1.property2"
assert someGroovyObject."$properties" == someValue
This is automatically recursive, and you don't have to explicitly call a method. This is only a getter, but you can define a second version with parameters to make a setter as well.
The downside is that, as far as I can tell, you can only define one version of propertyMissing, so you have to decide if dynamic path navigation is what you want to use it for.
See
https://stackoverflow.com/a/15632027/2015517
It uses ${} syntax that can be used as part of GString

Resources