Persisting Maps and Lists of properties as JSON in Grails - json

EDIT: onload() method changed to afterLoad(): Otherwise objects might not be passed properly to the map.
I am currently using some domain classes with a lot of dynamic, complex properties, that I need to persist and update regularly.
I keep these in a Map structure for each class since this makes it easy for referencing in my controllers etc.
However, since Grails does not seem to be able to persist complex property types like List and Map in the DB I am using the following approach to achieve this via JSON String objects:
class ClassWithComplexProperties {
Map complexMapStructure //not persisted
String complexMapStructureAsJSON //updated and synched with map via onload,beforeInsert,beforeUpdate
static transients = ['complexMapStructure']
def afterLoad() { //was previously (wrong!): def onLoad() {
complexMapStructure=JSON.parse(complexMapStructureAsJSON)
}
def beforeInsert() {
complexMapStructureAsJSON= complexMapStructure as JSON
}
def beforeUpdate() {
complexMapStructureAsJSON= complexMapStructure as JSON
}
static constraints = {
complexMapStructureAsJSON( maxSize:20000)
}
}
This works well as long I am only loading data from the DB, but I run into trouble when I want to save back my changes to the DB. E.g. when I do the following
/* 1. Load the json String, e.g. complexMapStructureAsJSON="""{
data1:[[1,2],[3,4]],//A complex structure of nested integer lists
data1:[[5,6]] //Another one
}""" :
*/
ClassWithComplexProperties c=ClassWithComplexProperties.get(1)
// 2. Change a value deep in the map:
c.complexMapStructure.data1[0][0]=7
// 3. Try to save:
c.save(flush:true)
This will usually not work, since, I guess(?), GORM will ignore the save() request due to the fact that the map itself is transient, and no changes are found in the persisted properties.
I can make it work as intended if I hack step 3 above and change it to:
// 3.Alternative save:
complexMapStructureAsJSON="" //creating a change in persisted property (which will be overwritten anyway by the beforeUpdate closure)
c.save(flush:true)
To me this is not a very elegant handling of my problem.
The questions:
Is there a simpler approach to persist my complex, dynamic map data?
If I need to do it the way I currently do, is there a way to avoid the hack in step 3 ?

For option 2, you can use the beforeValidate event instead of beforeInsert and beforeUpdate events to ensure that the change propagates correctly.
class ClassWithComplexProperties {
Map complexMapStructure //not persisted
String complexMapStructureAsJSON //updated and synched with map via onload,beforeInsert,beforeUpdate
static transients = ['complexMapStructure']
def onLoad() {
complexMapStructure=JSON.parse(complexMapStructureAsJSON)
}
// >>>>>>>>>>>>>>
def beforeValidate() {
complexMapStructureAsJSON= complexMapStructure as JSON
}
// >>>>>>>>>>>>>>
static constraints = {
complexMapStructureAsJSON( maxSize:20000)
}
}

I of course do not know much about the application you are building, but it won't hurt to look up alternate data storage models particularly NOSQL databases. Grails has got some support for them too.

Is there a simpler approach to persist my complex, dynamic map data?
Grails can persist List and Map out of the box, you don't need to write complex conversion code and abuse Json.
Example for Map:
class ClassWithComplexProperties {
Map<String, String> properties
}
def props = new ClassWithComplexProperties()
props.properties = ["foo" : "bar"]
props.save()
Example for List:
class ClassWithComplexProperties {
List<String> properties
static hasMany = [properties: String]
}
def props = new ClassWithComplexProperties()
props.properties = ["foo", "bar"]
props.save()
I think this is much easier and cleaner way how to deal with it.

In response to
Is there a simpler approach to persist my complex, dynamic map data?
Grails can persist Sets, Lists and Maps to the database. That may be a simpler approach than dealing with JSON conversions. To have the map persisted to the database you need to include it in the hasMany property.
Map complexMapStructure
static hasMany = [complexMapStructure: dynamicComplexPropertyObject]
The documentation suggests that using a Bag may be more efficient.

Related

Dart: Generic which has .fromJson constructor

In Swift I'm used to setting up a protocol JSONInitializable which defines an init?(json: JSON) meaning that all classes and structs that conform to the protocol are initializable with a JSON object.
Is it true that this isn't possible in Dart (with an abstract class) because static methods and factory initializers are not inherited from an abstract class?
The reason I'm asking is because I'm writing a lot of similar methods for API GET requests, which could easily be merged into one, e.g.:
static Future<T> get<T extends JSONInitializable>(int id) async {
final resourceName = T; // TODO: transform to snake case
final uri = Uri.parse("$kApiHostname/api/$resourceName/$id");
final response = await _secureGet(uri);
if (response == null) {
return null;
}
final responseJson = json.decode(response.body);
final model = T.fromJson(responseJson);
return model;
}
But for that to work I'd need to constrain T to a protocol/interface that defines the .fromJson() initializer.
The feature you want is not available (or planned) in Dart, but there have been discussions on it.
Give this issue a thumbs up: https://github.com/dart-lang/language/issues/356
A MANUAL workaround could be having a map of serializers,deserializers like:
//Register object 1 in singleton
JsonMapper.register<MyObject>(JsonObjectMapper(
(mapper, map) => MyObject.fromJson(map),
(mapper, instance) => instance.toJson(),
));
//Register object 2 in singleton...
...
This way you can deserialize and serialize your objects as long as you have them registered without resorting to it's generic type.
The AUTOMATIC (technically code generated) way would be using a package like simple_json to help you workaround this with a little code generation so you don't mess up registering and eliminating mappers.
And the big plus is that the actual code that transforms your objecto from and to JSON is not stored in the object itself but in the generated classes, thus pushing the responsability of serializaition deserailization from the object into the generated code (in a decoupled manner).
Take my advice with a grain of salt as with both approaches you lose static type checking of wether a type has a registered mapper and can be converted.

EF serialize object with nav propertyes

I'm creating an API, and I need to send the data model to the client, for easier processing, so I don't need to implement and maintain the json structure on the client side too, just serialize a new empty object, and give it to the client.
But there comes my problem, if the nav property objects are empty, it get
serialized to an empty array
[]
not to the object in the array.
So my question is, can anyone point me to the right direction, to serialize the object with related properties, like how the swagger documentation does it?
Example for base serialization in MVC: http://pastebin.com/ihF2AeKY
Example for serialization by swagger with nav props, and types: http://pastebin.com/SxxztnDa
Now, I can instantiate the nav props, and add it to the object, then serialize it, but that is clumsy, and not following changes in the objects automatically, like the swagger example does.
EDIT:
This would be ideal, since if i change the base object, or prop relations, the model will automatically follow, but since properties are empty in a new object, unless i populate it by hand, the navigational props will be either null, or an empty list like '[]', like shown in the base example, so i won't get the nav props 'scheme', just the base object. Swagger doing some kind of magic here, to build the full tree, shown in the 2nd pastebin.
[Route("getmodel")]
[HttpGet]
public async Task<Company> GetContactCompanyModel(){
var ret = new Company();
return ret;
}
This is kind of working, but clumsy since i need to insert the props with hand, but this will return the correct model, with populated but empty navigational props.
[Route("getmodel")]
[HttpGet]
public async Task<Company> GetContactCompanyModel(){
var ret = new Company();
ret.Peoples.Add(new People { Address = new Address(), PrivateContact = new ContactDetails(), WorkContact = new ContactDetails()});
ret.ServicePlaces.Add(new ServicePlace { ContactDetails = new ContactDetails(), Address = new Address() });
return ret;
}
This is the output of my hand made new() boilerplate :
http://pastebin.com/C4W6Tvsg as you can see, it contains the empty object for the navigational properties, kinda like how EF would return it for me, so i can just send it to the client, and process it with Angular, even if i add new properties, or fields to it.
So in the end, the question is how can i traverse the object graph, and make a schema from it, kinda like if i would eager load it, but with empty objects.
Hope it's a bit clearer like this.

SpringBatch - how to set up via java config the JsonLineMapper for reading a simple json file

How to change from "setLineTokenizer(new DelimitedLineTokenizer()...)" to "JsonLineMapper" in the first code below? Basicaly, it is working with csv but I want to change it to read a simple json file. I found some threads here asking about complex json but this is not my case. Firstly I thought that I should use a very diferent approach from csv way, but after I read SBiAch05sample.pdf (see the link and snippet at the bottom), I understood that FlatFileItemReader can be used to read json format.
In almost similiar question, I can guess that I am not in the wrong direction. Please, I am trying to find the simplest but elegant and recommended way for fixing this snippet code. So, the wrapper below, unless I am really obligated to work this way, seems to go further. Additionally, the wrapper seems to me more Java 6 style than my tentative which takes advantage of anonimous method from Java 7 (as far as I can judge from studies). Please, any advise is higly appreciated.
//My Code
#Bean
#StepScope
public FlatFileItemReader<Message> reader() {
log.info("ItemReader >>");
FlatFileItemReader<Message> reader = new FlatFileItemReader<Message>();
reader.setResource(new ClassPathResource("test_json.js"));
reader.setLineMapper(new DefaultLineMapper<Message>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames(new String[] { "field1", "field2"...
//Sample using a wrapper
http://www.manning.com/templier/SBiAch05sample.pdf
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.JsonLineMapper;
import com.manning.sbia.ch05.Product;
public class WrappedJsonLineMapper implements LineMapper<Product> {
private JsonLineMapper delegate;
public Product mapLine(String line, int lineNumber) throws Exception {
Map<String,Object> productAsMap
= delegate.mapLine(line, lineNumber);
Product product = new Product();
product.setId((String)productAsMap.get("id"));
product.setName((String)productAsMap.get("name"));
product.setDescription((String)productAsMap.get("description"));
product.setPrice(new Float((Double)productAsMap.get("price")));
return product;
}
public void setDelegate(JsonLineMapper delegate) {
this.delegate = delegate;
}
}
Really you have two options for parsing JSON within a Spring Batch job:
Don't create a LineMapper, create a LineTokenizer. Spring Batch's DefaultLineMapper breaks up the parsing of a record into two phases, parsing the record and mapping the result to an object. The fact that the incoming data is JSON vs a CSV only impacts the parsing piece (which is handled by the LineTokenizer). That being said, you'd have to write your own LineTokenizer to parse the JSON into a FieldSet.
Use the provided JsonLineMapper. Spring Batch provides a LineMapper implementation that uses Jackson to deserialize JSON objects into java objects.
In either case, you can't map a LineMapper to a LineTokenizer as they accomplish two different things.

Grails 2.5.0 static compilation, controllers and grails features

I am testing out Grails static compilation, specifically GrailsCompileStatic. The documentation is limited in explaining what Grails dynamic features aren't supported. My test Controller is very simple, but I'm running into problems already.
#GrailsCompileStatic
class UserController {
UserService userService
def list() {
def model = [:]
def model = request.JSON
withFormat {
json {
render(model as JSON)
}
}
}
}
When compiling the application I get two compile time errors. The first about a missing property for JSON on the request object, and a second error about a missing method for json in the withFormat closure.
Seems to me I'm either doing something wrong or GrailsCompileStatic doesn't work with these features?
About request.JSON
The request object's getJSON() method is added via the ConvertersPluginSupport class. The exact lines are:
private static void enhanceRequest() {
// Methods for Reading JSON/XML from Requests
def getXMLMethod = { -> XML.parse((HttpServletRequest) delegate) }
def getJSONMethod = { -> JSON.parse((HttpServletRequest) delegate)}
def requestMc = GrailsMetaClassUtils.getExpandoMetaClass(HttpServletRequest)
requestMc.getXML = getXMLMethod
requestMc.getJSON = getJSONMethod
}
As you can see it uses the dynamic dispatch mechanism, but fortunately it's not such a big deal. You can simply replicate it by executing JSON.parse(request) anywhere in your controller.
Pay attention though! JSON.parse(HttpServletRequest) returns an Object, which is either a JSONObject or a JSONArray, so if you plan on using them explicitly, and you are compiling statically, you will have to cast it.
You might create a common base class for your controllers:
import org.codehaus.groovy.grails.web.json.JSONArray
import org.codehaus.groovy.grails.web.json.JSONObject
import grails.converters.JSON
#GrailsCompileStatic
class BaseController {
protected JSONObject getJSONObject() {
(JSONObject) JSON.parse(request)
}
protected JSONArray getJSONArray() {
(JSONArray) JSON.parse(request)
}
}
Then in your controller you can simpy invoke getJSONObject() or getJSONArray. It's a bit of a workaround, but results in a staticly compileable code.
About withFormat
This is a bit more complicated. The withFormat construct is really a method, which has a Closure as it's first parameter. The internal implementation then figures out based on the current request or response content type which part of the argument closure is to be used.
If you want to figure out how to do this statically, take a look at the source code.
You could extend this class, then use it's protected methods, but I don't know if it's worth all the hussle, you would loose much of Grails' conciseness. But if you really want to do it, you can. Don't you just love open source projects ? :)

Reflectively save domain class instances in Grails

The problem is as follows: I want to handle a POST request with JSON body. The body consists of an array of JSON Objects, without further nesting, i.e. simple HashMaps. All of these objects represent JSON-serialized domain classes from an Android Application, which will have their counterpart in my Grails app. I am thinking of parsing the JSON body, iterating through every element and saving each node as its corresponding domain class instance.
a) How should I save the instance? I am quite new to Grails/Groovy so please excuse any huge mistakes. Code so far is
public static Object JSONArray2Instances(String json, Class type) {
def slurper = new JsonSlurper()
def result = slurper.parseText(json)
//we only want to parse JSON Arrays
if (!(result instanceof JSONArray))
return null
result.each {
def instance = it.asType(type)
// now I need to save to domain class!
}
}
b) where do I place the corresponding code? Currently it is in /grails-app/src/groovy. Where do the tests go? (Since it is not a 'real' Grails component)
c) Is an intermediate command object more appropriate?
Your code should go in to the controller which is handling the request. Please take a look at
gson-grails plugin which has examples of how to serialize and deserialze objects and map them to domain objects. Please take a look at the grails basics where they talk about the conventions used in the grails application and the layout. There are good examples at grails site. Hope this helps
I solved my problem as follows, based on help provided by the comment from allthenutsandbolts. : (Grails-Gson plugin was not needed)
Let N2696AdminAction be the name of a Domain Class
in my controller:
class N2696AdminActionController extends RestfulController{
static responseFormats = ['json', 'xml']
def JSONHandlerService
N2696AdminActionController() {
super(N2696AdminAction)
}
#Override
#Transactional
def save(){
if (request!=null)
JSONHandlerService.instancesfromJSON(request.JSON)
}
}
then I delegate persisting to my service as follows
class JSONHandlerService {
def instancesfromJSON(Object request){
//we only want to parse JSON Arrays
if (!(request instanceof JSONArray))
return null
request.each {
def domainClass = Class.forName("${it.type}",
true, Thread.currentThread().getContextClassLoader())
def newDomainObject = domainClass.newInstance(it)
newDomainObject.save(failOnError:true, flush:true, insert: true)
}
}
}
type is a Json attribute which holds the full (package inclusive) name for my class. This way, I can save to multiple Domain Classes with the same POST request.