Grails: JSONP callback without id and class in JSON file - class

I am working on a REST based interface where people get a json file. The client needs to access the file from another Domain. I use jsonp which works so far. My problem is the rendering in Grails. At the moment I use the 'as JSON' to marshalling the object:
render "${params.jsoncallback}(${user as JSON})"
The Json file getting to the client inclused all attributes, incluing the id and class, which I do not want to have in there. In case it is not jsonp, I do it this way, which works great:
render(contentType:'text/json'){
userName user.userName
userImage user.userImage
:
:
}
So how do I get the id and class attributes out of the json when rendering "user as JSON"? Any idea?
best regards,
Klaas

You can get rid of the class and id properties in the JSON result by creating a custom ObjectMarshaller.
// CustomDomainMarshaller.groovy in src/groovy:
import grails.converters.JSON;
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller;
import org.codehaus.groovy.grails.web.json.JSONWriter;
import org.springframework.beans.BeanUtils;
public class CustomDomainMarshaller implements ObjectMarshaller<JSON> {
static EXCLUDED = ['metaClass','class','id','version']
public boolean supports(Object object) {
return ConverterUtil.isDomainClass(object.getClass());
}
public void marshalObject(Object o, JSON json) throws ConverterException {
JSONWriter writer = json.getWriter();
try {
writer.object();
def properties = BeanUtils.getPropertyDescriptors(o.getClass());
for (property in properties) {
String name = property.getName();
if(!EXCLUDED.contains(name)) {
def readMethod = property.getReadMethod();
if (readMethod != null) {
def value = readMethod.invoke(o, (Object[]) null);
writer.key(name);
json.convertAnother(value);
}
}
}
writer.endObject();
} catch (Exception e) {
throw new ConverterException("Exception in CustomDomainMarshaller", e);
}
}
}
You'll need to register in you grails-app/conf/BootStrap.groovy:
class BootStrap {
def init = { servletContext ->
grails.converters.JSON.registerObjectMarshaller(new CustomDomainMarshaller())
}
def destroy = {}
}
This should work in Grails >= 1.1

thanks for the 'quick' reply!
Man, it looks so easy in the end and took so long to figure out.
I got it working doing a map out of the values I needed and rendered them 'as json' like this:
def userProfile = user.get(randomUser)
def jsonData = [
username: userProfile.userName,
userimage: userProfile.userImage,
userstreet: userProfile.userStreet,
:
:
] as JSON
println jsonData
voila, there was the json I needed :)

It doesn't seem to me as if the JSON auto marshaller supports this.
You could use FlexJSON which allows you to exlude certain properties and wrap it into a custom Codec.
Also see here.

Related

Typescript - Get uninitialized properties after compilation

I am currently writing a wrapper around socket.io. Comming from a very object-oriented background, I want to implement the concept of Models in my framework/wrapper.
If you happen to know socket.io you might know that you get the data that is associated with an event as a parameter, now I have implemented a custom routing system where the handler of the route gets the data in an express.js like request object.
The idea is to have model classes that look something like this:
class XRequestModel
#v.String({ message: 'The username must be a string!' })
public userName: string;
}
And the route event might look something like this:
#RouteConfig({ route: '/something', model: XRequestModel })
class XEvent extends Route {
public on(req: Request<XRequestModel>, res: Response) {
// Handle Event
}
}
And to complete the example here is how the request object might look like:
class Request<T> {
public data: T;
}
Now generics in typescript are very limited since the type information is removed after compilation, I can not use the generic Request parameter ( which is the type of the model ) to get metadata from the model - Metadata, in this case, is the validation decorator. To overcome this issue I give a reference of the Model class to the RouteConfig of the RouteEvent, which is internally used and would allow me to create instances of the model, get the properties and so on...
The idea here is to give the handler of a route, a request object with pre-validated, typesafe data.
The thing holding me back from this, is the fact that unused properties, get removed after compilation by typescript, So I cannot get the metadata of the model. Initializing the class-property would solve this:
class XRequestModel
#v.String({ message: 'The username must be a string!' })
public userName: string = '';
}
But I think this makes for some very verbose syntax, and I dont want to force the user of this wrapper to init all the model properties.
An implementation side-note:
The user of the framework has to register the classes to a 'main' class and from there I can get the Route-class via decorator reflection.
When I try to get the properties of the model without initialized properties - First model example.
// Here the route.config.model refers to the model from the RouteConfig
Object.getOwnPropertyNames(new route.config.model());
>>> []
Here is what I get with initialized properties:
Object.getOwnPropertyNames(new route.config.model());
>>> [ 'userName' ]
Here a link to the GitHub repository: https://github.com/FetzenRndy/SRocket
Note that models are not implemented in this repo yet.
Basically, my question is: How can I get the properties of a class that has uninitialized properties after compilation.
The problem is that if no initialization happens, no code is emitted for the fields, so at runtime the field does not exist on the object until a value is assigned to it.
The simplest solution would be to initialize all fields even if you do so with just null :
class XRequestModel {
public userName: string = null;
public name: string = null;
}
var keys = Object.getOwnPropertyNames(new XRequestModel())
console.log(keys); // [ 'userName', 'name' ]
If this is not a workable solution for you, you can create a decorator that adds to a static field on the class and the walk up the prototype chain to get all fields:
function Prop(): PropertyDecorator {
return (target: Object, propertyKey: string): void => {
let props: string[]
if (target.hasOwnProperty("__props__")) {
props = (target as any)["__props__"];
} else {
props = (target as any)["__props__"] = [];
}
props.push(propertyKey);
};
}
class XRequestModelBase {
#Prop()
public baseName: string;
}
class XRequestModel extends XRequestModelBase {
#Prop()
public userName: string;
#Prop()
public name: string;
}
function getAllProps(cls: new (...args: any[]) => any) : string[] {
let result: string[] = [];
let prototype = cls.prototype;
while(prototype != null) {
let props: string[] = prototype["__props__"];
if(props){
result.push(...props);
}
prototype = Object.getPrototypeOf(prototype);
}
return result;
}
var keys = getAllProps(XRequestModel);
console.log(keys);

how get xml responce using JAX-WS SOAP handler

I have implemented web service:
#WebServiceClient(//parameters//)
#HandlerChain(file = "handlers.xml")
public class MyWebServiceImpl {...}
Also I have implemented ObjectFactory with list of classes for creating my requests and responses. For Example class Test.
I need to get xml of response.
I try to use JAX-WS SOAP handler, so I add this #HandlerChain(file = "handlers.xml") anotation.
My handlers.xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-class>java.com.webservice.service.LoggingHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
My LoggingHandler class is:
import java.io.PrintWriter;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class LoggingHandler implements javax.xml.ws.handler.soap.SOAPHandler<SOAPMessageContext> {
public void close(MessageContext messagecontext) {
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleFault(SOAPMessageContext messagecontext) {
return true;
}
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
PrintWriter writer = new PrintWriter("soap_responce" + System.currentTimeMillis(), "UTF-8");
writer.println(message);
writer.close();
message.writeTo(System.out);
System.out.println(""); // just to add a newline
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
return outboundProperty;
}
}
I have test class which creates request, here are part of code:
MyWebServiceImpl impl = new MyWebServiceImpl(url, qName);
ws = impl.getMyWebServicePort();
Test req = new Test();
I suppose to get xml response in file "soap_responce" + System.currentTimeMillis(). But such file isn't even created. Please suggest how to get xml response, I'm new to web services and may do something wrong. Thanks
Using SOAP handlers is IMHO perfectly fine for such a task. I would approach it the same way.
I was able to use your configuration with minor modifications to get the example running. As a result I am able to see generated files. If you can't see them please check whether you check correct path, e.g. using:
File file = new File("soap_responce" + System.currentTimeMillis());
System.out.println(file.getAbsolutePath());
What I changed is:
package from java.com.webservice.service.LoggingHandler to com.webservice.service.LoggingHandler as packages starting with java are forbidden
Complete project can be found here:
https://github.com/destin/SO-answers/tree/master/SO-how-get-xml-responce-using-jax-ws-soap-handler
org.dpytel.jaxws.jaxws_java_first_jboss.client.Main class shows how I get and execute the web service.
BTW. you don't need to implement client stub and object factory etc when you have WSDL file. You can use wsimport tool. You can check how to use it in mentioned project.

Jackson and REST Android Annotations: Deserialize JSON Array of Objects

I have a REST service which returns data that looks like this:
[
{ bookmark object json here },
{ bookmark object json here },
{ bookmark object json here },
...
]
My REST client class looks like this:
#Rest(rootUrl = Constants.ApiConfig.API_ROOT, converters = {MappingJackson2HttpMessageConverter.class})
public interface RestApiClient {
#Get("/bookmark/read?id={identifier}")
public BookmarkList getBookmarks(String identifier);
}
BookmarkList looks like this:
public class BookmarkList {
List<Bookmark> bookmarks;
#JsonValue
public List<Bookmark> getBookmarks() {
return bookmarks;
}
#JsonCreator
public void BookmarkList(#NotNull List<Bookmark> bookmarks) {
this.bookmarks = bookmarks;
}
}
However, when I utilize this setup, I get the following error:
Could not read JSON: Can not deserialize instance of com.whatever.entity.BookmarkList out of START_ARRAY token
What I want is something like the EventList example at https://github.com/excilys/androidannotations/wiki/Rest-API#get, but that doesn't seem to work for me out of the box.
Is there a way to get this working?
Ho... We have to update this part of documentation. The wrapper solution works but doesn't fit APIs.
If you're looking at the generated code for #Get("url") MyReturnedType testService(), you should see something like this :
return restTemplate.exchange(rootUrl.concat("url"), //
HttpMethod.GET, //
null, //
MyReturnedType.class, //
urlVariables)//
.getBody();
The returned class is injected as a parameter of exchange call. In case of generics collection (like List<MyReturnedType>), we can't inject List.class because of type checking in the return of exchange method.
However, you should be able to use this little trick in your #Rest annotated method :
public class BookmarkList extends List<Bookmark> {
}
I think I misunderstood the example at https://github.com/excilys/androidannotations/wiki/Rest-API#get. I think the array still must be wrapped inside a JSON object in that example (It'd be nice if they included example JSON data).
The data the service I'm connecting to does not return an object wrapping the array like that, so, I altered the REST client to look like this:
#Rest(rootUrl = Constants.ApiConfig.API_ROOT, converters = {MappingJackson2HttpMessageConverter.class})
public interface RestApiClient {
#Get("/bookmark/read?id={identifier}")
public ArrayNode getBookmarks(String identifier);
}
And I wrote a method in another class to iterate the ArrayNode and build the bookmarks:
public List<Bookmark> getBookmarks(Content content) {
ArrayList<Bookmark> bookmarks = new ArrayList<Bookmark>();
ArrayNode bookmarksData = apiClient.getBookmarks(content.getAcid());
for(JsonNode bookmarkData : bookmarksData) {
Bookmark bookmark = objectMapper.convertValue(bookmarkData, Bookmark.class);
bookmarks.add(bookmark);
}
return bookmarks;
}
So it's not as convenient (I had to write more code myself), but I got it working.

Losing type element when serializing object inside ArrayList to XML

I seem to be experiencing a problem when using Jackson to serialize to XML. My code is below:
TEST CONTAINER
package com.test;
import java.util.ArrayList;
import com.fasterxml.jackson.annotation.JsonProperty;
public class TestContainer {
private String testContainerID;
private String testContainerMessage;
private ArrayList<TestChild> testContainerChildren;
#JsonProperty("TestContainerID")
public String getTestContainerID() {
return testContainerID;
}
#JsonProperty("TestContainerID")
public void setTestContainerID(String testContainerID) {
this.testContainerID = testContainerID;
}
#JsonProperty("TestContainerMessage")
public String getTestContainerMessage() {
return testContainerMessage;
}
#JsonProperty("TestContainerMessage")
public void setTestContainerMessage(String testContainerMessage) {
this.testContainerMessage = testContainerMessage;
}
#JsonProperty("TestContainerChildren")
public ArrayList<TestChild> getTestContainerChildren() {
return testContainerChildren;
}
#JsonProperty("TestContainerChildren")
public void setTestContainerChildren(ArrayList<TestChild> testContainerChildren) {
this.testContainerChildren = testContainerChildren;
}
}
TESTCHILD
package com.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonRootName(value="TestChild")
public class TestChild {
private String testChildID;
private String testChildMessage;
#JsonProperty("TestChildID")
public String getTestChildID() {
return testChildID;
}
#JsonProperty("TestChildID")
public void setTestChildID(String testChildID) {
this.testChildID = testChildID;
}
#JsonProperty("TestChildMessage")
public String getTestChildMessage() {
return testChildMessage;
}
#JsonProperty("TestChildMessage")
public void setTestChildMessage(String testChildMessage) {
this.testChildMessage = testChildMessage;
}
}
USE
Serialization:
XmlMapper xm = new XmlMapper();
TestContainer tc = xm.readValue(sb.toString(), TestContainer.class);
Deserialization:
System.out.println(xm.writeValueAsString(tc));
tc = xm.readValue(sb.toString(), TestContainer.class);
What I'm doing is loading an XML file from a folder on the classpath and putting the contents of the file into a StringBuffer. The problem is the generated XML for the collection of objects. When writing the XML, I want something like:
<TestContainerChildren><TestChild><...(Element Details)...></TestChild></TestContainerChildren>
but I'm getting:
<TestContainerChildren><TestContainerChildren><...(Element Details)...><TestContainerChildren></TestContainerChildren>
I'm not sure what I'm missing, here. I have no problem with the JSON part of the serialization/deserialization, only the XML. I've tried using both Jackson and JAXB annotations to turn off wrapping, I have tried using the following annotations:
#JsonRootName
#JsonProperty
#JacksonXmlElementWrapper
#JacksonElement
#XmlElementWrapper
#XmlElement
I'm pretty sure this is something stupid on my part, but any help would be most appreciated.
Ok, couple of notes. First, #JsonRootName only affects name used for the root of XML document, as name implies. So it is not used for TestChild. Second, it sounds like you want to use so-called "unwrapped" output for Lists, omitting element for property that contains List elements. This is doable with:
#JacksonXmlElementWrapper(useWrapping=false)
#JsonProperty("TestContainerChildren")
public ArrayList<TestChild> getTestContainerChildren() { ... }
since default setting is to use wrapper (this is different from JAXB, where unwrapped is the default). Or, if you want to change this globally to assume unwrapped as default, you can change the defaults via XmlModule:
JacksonXmlModule module = new JacksonXmlModule();
// to default to using "unwrapped" Lists:
module.setDefaultUseWrapper(false);
XmlMapper xmlMapper = new XmlMapper(module);
Hope this helps!
I got this working by using the following annotations above the variable declaration:
#JacksonXmlElementWrapper(localName="[insert collection name]")
#JacksonXmlProperty(localName="[insert collection element name]")
This was a simple case of RTFM, as it's documented here.

Persisting dynamic groovy properties with GORM MongoDB

I am currently trying to persist the following class with the GORM MongoDB plugin for grails:
class Result {
String url
def Result(){
}
static constraints = {
}
static mapWith="mongo"
static mapping = {
collection "results"
database "crawl"
}
}
The code I'm running to persist this class is the following:
class ResultIntegrationTests {
#Before
void setUp() {
}
#After
void tearDown() {
}
#Test
void testSomething() {
Result r = new Result();
r.setUrl("http://heise.de")
r.getMetaClass().setProperty("title", "This is how it ends!")
println(r.getTitle())
r.save(flush:true)
}
}
This is the result in MongoDB:
{ "_id" : NumberLong(1), "url" : "http://heise.de", "version" : 0 }#
Now the url is properly persisted with MongoDB but the dynamic property somehow is not seen by the mapper - although the println(r.getTitle()) works perfectly fine.
I am new to groovy so I thought that someone with a little more experience could help me out with this problem. Is there a way to make this dynamically added property visible to the mapping facility? If yes how can I do that?
Thanks a lot for any advice...
Rather than adding random properties to the metaClass and hoping that Grails will both scan the metaClass looking for your random properties and then persist them, why not just add a Map to your domain class, (or a new Key/Value domain class which Result can hasMany) so you can add random extra properties to it as you want.
try this doc
#Test
void testSomething() {
Result r = new Result();
r.url = "http://heise.de"
r.['title'] = "This is how it ends!" //edit: forgot the subscript
println r.['title']
r.save(flush:true)
}
BTW, Instead of using gorm or hibernate you can always use directly java api / gmongo.