The Grails 2.5.4 docs say that it's possible to exclude properties from rendering for an entire group of domain classes.
There are some default configured renderers and the ability to register or override renderers for a given domain class or even for a collection of domain classes.
However there's no example given in the docs for how to do this. Does anyone know how to exclude properties for all of my domain classes? Specifically I'm trying to get rid of the class and enumType fields that Grails automatically adds to the response body.
There doesn't seem to be any good way to do this. What I discovered is that if you register an exclusion for a super class, all subclasses also "inherit" that exclusion. So to get rid of four properties for all Groovy objects (which cover all domain classes), I added the following bean to resources.groovy.
groovyObjectJsonRenderer(JsonRenderer, GroovyObject) {
excludes = ['class', 'declaringClass', 'errors', 'version']
}
I don't know if you are talking about this, but you can ignore some properties when you render as JSON, overriding the Marshaller here is the code:
static {
grails.converters.JSON.registerObjectMarshaller(NAMEOFYOURCLASS) {
return it.properties.findAll {k,v -> k != 'class' && k!='declaringClass'}
}
}
or if you want to create your custom render you can do something like this
static {
grails.converters.JSON.registerObjectMarshaller(NAMEOFYOURCLASS) {
def lista = [:]
lista['id'] = it.id
lista['name'] = it.name
lista['dateCreated'] = it.date?.format("dd/MM/yyyy HH:mm")
return lista
}
}
You can put it where you think its better i actually prefer to put it in the class i'm overriding, because letter i can find it or if some one else it's looking the code, he/she can find it easy.
Related
I followed the demo project here https://codeeffects.com/Doc/Business-Rule-Flex-Source for a sample of how the Flex properties work. My rules are evaluated correctly, but the editor does not show the fields.
I have looked at all of the properties of the FlexPropertyInfo object and confirmed that they set as I would expect. What else should I try in order to troubleshoot this?
Make sure that your Flex type inherits from Type and implements the minimum required properties and methods, and GetProperties in particular, e.g.:
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
List<FlexPropertyInfo> properties = new List<FlexPropertyInfo>();
properties.Add(new FlexPropertyInfo("Id", typeof(int), typeName));
properties.Add(new FlexPropertyInfo("Name", typeof(string), typeName));
return properties.ToArray();
}
Make sure that you pass a new instance of your Flex type and not the type itself.
RuleEditor editor = new RuleEditor("divRuleEditor")
{
Mode = Common.RuleType.Execution,
SourceType = new MyFlexType() //and not typeof(MyFlexType)
}
The Editor itself knows nothing about Flex. As far as it is concerned it is another type and it is using reflection to pull the list of all members. Which is why it is important to pass an instance of your type. Otherwise you will get reflection on the Type class instead. Same goes for properties and other Flex objects.
Say we have:
class MyClass {
myProperty: string
}
Is there any built in function or easy way to get JSON like this?:
{
"myProperty": "string"
}
EDIT: My end goal is I want to dynamically print typed class definitions to a web view, in some kind of structured object syntax like JSON. I'm trying to make a server API that will return the schema for various custom classes - for example http://myserver.com/MyClass should return MyClass's properties and their types as a JSON string or other structured representation.
Evert is correct, however a workaround can look like this
class MyClass {
myProperty: string = 'string'
}
JSON.stringify(new MyClass) // shows what you want
In other words, setting a default property value lets TS compile properties to JS
If the above solution is not acceptable, then I would suggest you parsing TS files with your classes with https://dsherret.github.io/ts-simple-ast/.
Typescript class properties exist at build-time only. They are removed from your source after compiling to .js. As such, there is no run-time way to get to the class properties.
Your code snippet compiles to:
var MyClass = /** #class */ (function () {
function MyClass() {
}
return MyClass;
}());
As you can see, the property disappeared.
Based on your update, I had this exact problem. This is how I solved it.
My JSON-based API uses json-schema across the board for type validation, and also exposes these schemas for clients to re-use.
I used an npm package to automatically convert json-schema to Typescript.
This works brilliantly.
I know in grails i can define diferent JSON marshallers and asign names to them for different uses, which is very nice. However i end with a lot of code in the Bootstrap section and i end with two places where i need to tweak when domain classes changes.
Ithink thi is not good enough and i wonder if it might be possible to define JSON marshallers in the domain class itself.
Do you think would it be a good practice? ... can you provide suggestions on the best approach to achieve this?
Thanks,
I wrote a plugin for this purpose specifically. It allows you to use annotations in domain classes, like this:
import grails.plugins.jsonapis.JsonApi
class User {
static hasMany = [
pets: Pet
]
#JsonApi
String screenName
#JsonApi('userSettings')
String email
#JsonApi(['userSettings', 'detailedInformation', 'social'])
String twitterUsername
#JsonApi(['detailedInformation', 'userSettings'])
Set pets
String neverGetsSerialized
#JsonApi('detailedInformation')
Integer getNumberOfTicklyAnimals() {
pets.count { it.likesTickling }
}
}
In your controller, you would then call JSON.use('detailedInformation') to activate a specific marshaller.
In Bootstrap.groovy write this code:
JSON.registerObjectMarshaller(YourClass) { YourClass yourClass->
Map result = [:]
result['yourClass.property'] = yourClass.property
def domain = new DefaultGrailsDomainClass(YourClass)
domain.persistentProperties.each { GrailsDomainClassProperty property, String propertyName = property.name ->
result[propertyName] = yourClass[(propertyName)]
}
return result
}
Code below add one property, you can name it how u want
result['yourClass.property'] = yourClass.property
This code add all properties by it's name to map:
domain.persistentProperties.each { GrailsDomainClassProperty property, String propertyName = property.name ->
result[propertyName] = yourClass[(propertyName)]
}
I have a huge message file which i need to split into multiples files for different languages.
For example :
I created one folder for English locale i.e. en and another for French locale , fr inside conf folder.
en contains messages1_en.properties and messages2_en.properties
fr contains messages1_fr.properties and messages2_fr.properties
How to access these properties files inside my view.
Thanks
The only way to do that without introducing your own alternative implementation and use that instead of the built in Messages is to use hacked locales, so you would do fr_type1, fr_type2 or something like that to select the right alternative.
This is probably a bad idea since it's always risky to use an API in a different way from how it was intended to be used, there is a high risk of unexpected behaviour and it might be brittle since there is no guarantee that you will be able to use made up locales in future versions etc.
If you look at the Messages implementation you could probably get some ideas of how to implement your own without much fuss.
Good luck!
That's an old question, but i had a close issue, and i didn't find a solution anywhere.
This example use a configuration key to load messages from a file with a custom name. But you can easily modify it to load messages file from a subdirectory and/or multiple messages files.
Override play.api.i18n.DefaultMessagesApiProvider
#Singleton
class CustomMessagesApiProvider #Inject() (
environment: Environment,
config: Configuration,
langs: Langs,
httpConfiguration: HttpConfiguration)
extends DefaultMessagesApiProvider(environment, config, langs, httpConfiguration) {
def filename =
config.get[String]("play.i18n.filename")
override protected def loadAllMessages: Map[String, Map[String, String]] = {
langs.availables.map(_.code).map { lang =>
(lang, loadMessages(filename +"." + lang))
}.toMap
.+("default" -> loadMessages(filename))
.+("default.play" -> loadMessages(filename+".default"))
}
}
Add Guice binding in Module.java
#Override
public void configure() {
bind(DefaultMessagesApiProvider.class).to(CustomMessagesApiProvider.class);
}
It's my first Scala class, so maybe it can be improved. But it works.
To load multiple files (it compiles but not tested)
override protected def loadAllMessages: Map[String, Map[String, String]] = {
langs.availables.map(_.code).map { lang =>
(lang,
loadMessageFiles("." + lang))
}.toMap
.+("default" -> loadMessageFiles(""))
.+("default.play" -> loadMessageFiles(".default"))
}
private def loadMessageFiles(suffix: String) = {
loadMessages("messages-1" + suffix) ++ loadMessages("messages-2" + suffix)
}
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.