Grails XML marshalling: change default "<list>" root element name - java

By default Grails renders List in XML with a <list> element tag at its root. Likewise it renders Map with <map>. I would like to control the name of the root element.
If I'm returning an ArrayList of User, then I'd like to see:
<users>
<user>...</user>
<user>...</user>
</users>
How can I achieve the above? Here are the Requirements:
Easy to apply this serialization for 50+ domain classes
Abstracted from developers so no explicit coding is required during rendering domain objects (i.e., when render() or respond() is invoked, an ArrayList is still passed in, no explicit casting/converting like as MyNewType)
Able to handle the edge case of an empty list (should return <users/>)
Nice-to-haves:
If this formula can be applied to Map as well, great :)
I have been semi-successful in achieving the goals above, except I don't know how to account for the empty list case. I implemented my own ObjectMarshaller which renders all objects of type List. So long as the list contains one element, I can check the element's type and determine what the plural tag name should be (User => users). But if the list is empty, and since Java generics are by erasure (unless that's different in Groovy?) then I have no way to properly name an empty list other than defaulting to something like <list/>, which is not acceptable.
Some resources that I've been through:
http://www.cacoethes.co.uk/blog/groovyandgrails/dry-json-and-xml-with-grails
http://grails.1312388.n4.nabble.com/Custom-XML-Marshaller-change-the-root-element-name-td4649949.html
http://jwicz.wordpress.com/2011/07/11/grails-custom-xml-marshaller/
http://mrhaki.blogspot.com/2013/11/grails-goodness-register-custom.html
http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-object-marshallers/

A way to achieve this is to write a subclass for the CollectionMarshaller class and register it in our Grails application. We can for example register a custom implementation in BootStrap.groovy with the following code:
import org.codehaus.groovy.grails.web.converters.marshaller.xml.CollectionMarshaller
import grails.converters.XML
class BootStrap {
def init = { servletContext ->
// Register custom collection marshaller for List with User instances.
// The root element name is set to users.
XML.registerObjectMarshaller(new CollectionMarshaller() {
#Override
public boolean supports(Object object) {
object instanceof List<User>
}
#Override
String getElementName(final Object o) {
'users'
}
})
}
}
To make this work for more domain classes we might get a reference to all domain classes in BootStrap.groovy and loop through them to configure custom CollectionMarshaller instances.
For maps you can extend MapMarshaller
Also described in http://mrhaki.blogspot.com/2014/02/grails-goodness-customize-root-element.html

Related

How can my Freemarker ObjectWrapper access a template setting

Use case: system administrator stores a Freemarker template in a database which is used (by Spring Boot REST API) to present information stored by system users (respondents) in a locale-aware way to a different user type (reviewer).
A respondent's response might be stored in this sort of object (or in lists of this sort of object, in the event a question posed to the respondent is expected to have multiple answers):
// snip
import com.fasterxml.jackson.databind.node.ObjectNode;
// more imports snipped
public class LanguageStringMap {
private Map<Language, String> languageStringMap;
public LanguageStringMap(ObjectNode languageMapNode) {
// snip of code instantiating a LanguageStringMap from JSON
}
public void put(Language language, String value) {
if (value.length() == 0)
throw new IllegalArgumentException(String.format(
"value for language '%s' of zero length", language.getCode()));
languageStringMap.put(language, value);
}
public String get(Language language) { return languageStringMap.get(language); }
}
What I think I want to do is write an ObjectWrapper that maps instances of LanguageStringMap to a string (obtained by calling the get() method with a language derived from the Locale requested by the reviewer's browser and set in the template's settings). This presents a cleaner user experience to the system administrator than making the uploaded template contain a bunch of template method calls would.
To do this, my object wrapper needs to access a template setting. I have perused the pertinent Freemarker documentation, but I am still unclear on how to do this or if it is even possible.
I think it would be a mistake to try to implement this with resource bundles uploaded to the database alongside the templates, but that is a consideration.
Typically you simply put the locale specific string into the data-model before the template is processed, along with all the other variables. In that case no ObjectWrapper customization is needed. But if you have to use an ObjectWrapper-based solution, then you can get the locale inside an ObjectWrapper method (like in the override of DefaultObjectWrapper.handleUnknownType) with Environment.getCurrentEnvironment().getLocale().

How to set a Java Object as the datasource on a custom Control

I have a Java Bean with a Method that returns an Object of type PaymentItem
Payments.getItem(viewScope.vsRIndex);
this method returns the nth item from an ArrayList<PaymentItem>
I have a button on my main page that renders a Custom Control on that on the main page and sets the viewScope to the correct index value.
<xp:panel id="panelPaymentEntry"
rendered="#{javascript:(viewScope.vsShowPayment) ? true : false;}">
<xc:ccCOMPaymentInput></xc:ccCOMPaymentInput>
</xp:panel><!-- panelPaymentEntry -->
I want to set the dataSource for ccCOMPaymentInput to the PaymentItem returned by Payments.getItem(viewScope.vsRIndex)
I added this code to the createObject
try{
Payments.getItem(viewScope.vsRIndex);
}catch(e){
//do nothing
}
with the var = pItem
But does not appear that the Object pItem has been created.
Am I on the right track? or ?????
Generally, the cleanest way to do this is to create a custom property on the custom control to specify the context object - value is the conventional pick. So you'd have something like:
<xc:ccCOMPaymentInput value="#{javascript:Payments.getItem(viewScope.vsRIndex)}"/>
Then, within the control, you can reference it as compositeData.value. For example:
<xp:inputText value="#{compositeData.value.someTextField}"/>
The Object data source you're presumably referring to can also work, but isn't always necessary.

What's a good design pattern to implement a network protocol (XML)?

I want to implement a network protocol. To obtain a maintainable design I am looking for fitting patterns.
The protocol is based on XML and should be read with java. To simplify the discussion here I assume the example grammar:
<User>
<GroupList>
<Group>group1</Group>
<Group>group2</Group>
</GroupList>
</User>
Short question:
What is a good design pattern to parse such thing?
Long version:
I have found this and this question where different patterns (mostly state pattern) are proposed.
My actual (but lacking) solution is the folowing:
I create for each possible entry in the XML a class to contain the data and a parser. Thus I have User, User.Parser, ... as classes.
Further there is a ParserSelector that has a Map<String,AbstractParser> in which all possible subentries get registered.
For each parser a ParserSelector gets instantiated and set up.
For example the ParserSelector of the GroupList.Parser has one entry: The mapping from the string "Group" to an instance of Group.Parser.
If I did not use the ParserSleector class, I would have to write this block of code into every single parser.
The problem is now how to get the read data to the superobjects.
The Group.Parser would create a Group object with content group1.
This object must now be registered in the GroupList object.
I have read of using Visitor or Observer patterns but do not understand how they might fit here.
I give some pseudo code below to see the problem.
You see, that I have to check via instanceof for the type as statically there is the type information not available.
I thought this should be possible to solve using polymorphism in java in a cleaner (more maintainable) way.
I always face then the problem that java does only do dynamic binding on overriding.
Thus I cannot add a parameter to the XMLParser.parse(...) method to allow of "remote updating" as in a visitor/observer like approach.
Side remark: The real grammar is "deep" that is, it is such that there are quite many XML entries (here only three: User, GroupList and Group) while most of them might contain only very few different subentries (User and GroupList may only contain one subentry here, while Group itself contains only text).
Here comes some lines of pseude java code to explain the problem:
class User extends AbstractObject {
static class Parser implements XMLParser {
ParserSelector ps = ...; // Initialize with GroupList.Parser
void parse(XMLStreamReader xsr){
XMLParser p = ps.getParser(...); // The corresponding parser.
// We know only that it is XMLParser statically.
p.parse(...);
if(p instanceof GroupList.Parser){
// Set the group list in the User class
}
}
}
}
class GroupList extends AbstractObject{...}
class Group extends AbstractObject{...}
class ParserSelector{
Map<String,XMLParser> = new Map<>();
void registerParser(...){...} // Registers a possible parser for subentries
XMLParser getParser(String elementName){
return map.get(elementName); // Returns the parser registered with the given name
}
}
interface XMLParser {
void parse(XMLStreamReader xsr);
}
abstract class AbstractObject{}
To finish this question:
I ended up with JAXB. In fact I was not aware of the fact that it allows to easily create a XML Schema from java source code (using annotations).
Thus I just have to write the code with classical java objects which are used for transfer. Then the API handles the conversion to and from XML quite well.

GWT RequestFactory, Editors - working with tree-like structures

Let's say we have a domain entity defined this way:
interface MyNode {
MyNode getParent();
void setParent(MyNode node);
List<MyNode> GetChildren();
void AddChild(MyNode node);
void RemoveChild(MyNode node);
String getText();
void setText(String text);
}
I'm trying to implement a GWT web-app to work with these entities. I'm using request factory and editors framework. And I'm having some problems for sure :-)
Since the request factory definitions are trivial, I won't post them here. I'd only say that all the stuff related with children is a set of InstanceRequests.
So, the problem #1
Let's say we want to have a navigator for the whole tree. The idea is, every time we only see one node and we can either navigate to its parent or to one of its children. We'd like this navigator to use editors framework, so we build editors like MyNodeEditor and ChildrenListEditor.
As far as I know, editors only directly applicable to bean-styled entities. So, as long as working with MyNode text property is fine, working with children property (ChildrenListEditor) requires instance request.
My solution is, make MyNodeEditor to be a ValueAwareEditor and when it gets its value set, it initiates an InstanceRequest to get the list of child nodes. That list is then bound to ChildrenListEditor.
Are there any easier solutions? I believe it's quite a basic scenario.
Problem #2
We now decide to make our MyNodeEditor capable of editing. Bean-style properties are fine again, but what about children? Using the code mentioned in problem #1:
#Override public void setValue(MyNodeProxy value) {
...
requestFactory.myNodeRequest().getChildNodes().using(value).fire(new Receiver<List<MyNodeProxy>>() {
#Override public void onSuccess(List<MyNodeProxy> response) {
childrenDriver.display(response);
}
});
...
}
causes "Caused by: java.lang.IllegalArgumentException: Attempting to edit an EntityProxy previously edited by another RequestContext" because I'm having 2 different requests for the same entity here. I don't have access to RequestContext I've constructed at MyNodeEditor, so I'm constructing the new one and it fails. What's the right approach?
It'd be easier if you had a List<MyNodeProxy> getChildren() property on MyNodeProxy to access the children, rather than firing a distinct request.
You can access the RequestContext you passed to the RequestFactoryEditorDriver by implementing HasRequestContext on your editor. But in that case it won't help you, as firing it (from within your editor) would freeze it and thus make it unusable for anything else (such as saving the node after flushing the editor driver). If you cannot add a getChidren to your MyNodeProxy, then I'd suggest getting the children's list before you edit the node in the editor driver (alternatively, you could use a request based on the node's ID, rather than passing the node instance as an argument, or as a using() value, which is what's causing the error).

Binding spring:checkboxes to enumset on submit causes error

Just a heads up, I am using Java and Spring for a web app.
I have an object (objectBean) that contains an EnumSet (enumSet) of type EnumInnerObject as an attribute. I am passing this object as a bean from my controller to my .jsp view. I use the following .jsp code to bind the checkboxes:
<form:form commandName="objectBean" name="whatever" action="./save.htm" method="post">
<form:checkboxes items="${allOptions}" path="enumSet" />
</form:form>
Here is my controller initbinder:
#InitBinder
protected void initBinder(WebDataBinder binder) throws Exception{
binder.registerCustomEditor(EnumSet.class, "enumSet", new CustomCollectionEditor(Collection.class){
protected Object convertElement(Object element){
if(element instanceof String){
EnumInnerObject enumInnerObject= EnumInnerObject.valueOf((String)element);
return enumInnerObject;
}
return null;
}
});
In the controller, I pass allOptions (separate from my bean), and this contains all EnumInnerObject options, so all the checkboxes are displayed. "enumSet" is the EnumSet attribute with the appropriate values contained (if the value is contained in EnumSet, then it automatically checks the correct box in "allOptions"). All of this works and the .jsp correctly shows the correct checked boxes. However, the problem is when I submit the page to save. I get the following error:
java.lang.IllegalArgumentException: Cannot convert value of type [java.lang.String[]] to required type [java.util.EnumSet] for property 'enumSet': PropertyEditor [com.example.controller.MyController$1] returned inappropriate value]
I have a feeling I have to modify the InitBinder to get the form submit to work. Any ideas??
Thanks!
Frankly speaking, I can hardly imagine how this idea is gonna work: EnumSet collection is designed to store the values of enums, but at the moment it is constructed is needs to know the number of elements in that enum (= size of universe it its terms).
CustomCollectionEditor is passed a collection class as it's constructor argument, so it need to create this collection and it will fail for above reason. More over CustomCollectionEditor supports only the limited amount of target collections (ArrayList, TreeSet, LinkedHashSet, see CustomCollectionEditor#createCollection()).
In order not to overcomplicate things I suggest you to use common collections, rather than EnumSet. Otherwise you need to write your own property editor. The implementation won't be difficult, something like:
binder.registerCustomEditor(EnumSet.class, "enumSet",
new PropertyEditorSupport() {
#Override
public void setValue(Object value) {
EnumSet<EnumInnerObject> set = EnumSet.noneOf(EnumInnerObject.class);
for (String val: (String[]) value) {
set.add(EnumInnerObject.valueOf(val));
}
super.setValue(set);
}
});

Categories

Resources