-
1. Re: Custom serializers
alesj Oct 4, 2013 4:35 PM (in response to gregor.sfiligoj)Before answering this :-), how do you imagine serializers work?
As I actually misunderstood it the first time, hence CapeDwarf impl is probably wrong.
We do handle that, but it looks like it doesn't kick-in for you for some reason.
Do you have any .api file present in your deployment?
We actually don't read it, but they do serve us as marker files.
Can you provide a test for this?
Or attach a simple example?
-
2. Re: Custom serializers
gregor.sfiligoj Oct 9, 2013 8:06 AM (in response to alesj)You need serializers for customize the Java objects to JSON translation.
In my project for example, the Jackson implementation (which Appengine uses) marshall Java objects via reflection. In some situations, where objects have reference to other objects of same type for example, Jackson find a "circular reference" and returns an exception... In this case, using a custom serializer is possible to avoid this.
So in general, if I have an Example.class for which I'm not happy with the Jackson serialization, I write a custom ExampleSerializer.class and add it to serializers array of @Api annotation (or using @ApiSerializer annotation).
Give me some time to get the source and check what happens now with your implementation ;-)
A very simple example is in the Appengine documentation: https://developers.google.com/appengine/docs/java/endpoints/annotations#apiserializer
-
3. Re: Custom serializers
alesj Oct 9, 2013 8:52 AM (in response to gregor.sfiligoj)A very simple example is in the Appengine documentation: https://developers.google.com/appengine/docs/java/endpoints/annotations#apiserializer
Yeah, this one is completely broken. :-)
You're not allowed to have primitive types as input/output.
We already have a few tests here:
Can you add your serializer impl to this thread?
Anyway, I had a f2f with Endpoints guys, so I now know what to do. :-)
-
4. Re: Re: Custom serializers
gregor.sfiligoj Oct 9, 2013 9:51 AM (in response to alesj)"You're not allowed to have primitive types as input/output."
This is not true!
It is true, that you are not allowed to have primitive types, enums and also primitive "classes" (Integer, Long and so on) as input/output in API METHODS not objects returned as method result or useed as method input.
For example, for a Long returning type the Google client library generator creates a LongResponse class which has getter/setter for a Long type!
We need custom serializers for objects used as input arguments or responses from api methods. Don't miss this point!
So, the example in appengine documentation is working. They serialize a Bar class which CAN have any kind of java serializable type (from Jackson marshaller point of view, not by the Java Serializable subclass meaning...) Jackson marshaller is used for this task. And, as already told, if I'm not happy how Jackson serialize a class (or to avoid Jackson marshaller exceptions or whatsoever) I will write my custom serializer.
This is the simple serializer I use. I just need a safeString rapresentation of a Key instead of the whole serialized class in my client code:
package eu.novalike.diemmegi.web.server; import com.google.api.server.spi.config.Serializer; import com.googlecode.objectify.Key; @SuppressWarnings("rawtypes") public class KeySerializer implements Serializer<Key, String> { @Override public Key deserialize(String safeString) { return Key.create(safeString); } @Override public String serialize(Key key) { return key.getString(); } }
I hope that this better explains this topic :-)
-
5. Re: Re: Custom serializers
alesj Oct 9, 2013 10:13 AM (in response to gregor.sfiligoj)So, what's the exact response you get when returning Key?
I guess this KeySerializer, after your endpoint returns Key, takes over, and converts this into (safe) String.
But what's the final output?
It must be something JSON-like, and I'm wondering what it is.
Probably StringResponse?
(or something like that, if we follow Long' example)
-
6. Re: Re: Custom serializers
gregor.sfiligoj Oct 9, 2013 10:43 AM (in response to alesj)You return the String rapresentation of the class or whatsoever you are serializing.
For example, this is the JSON response from AppEngine to one of my methods:
{
"items" : [ {
"id" : "4638839557586944",
"nome" : "Nome 16",
"cognome" : "Cognome 16",
"azienda" : "Azienda 16 srl",
"indirizzo" : "Via del Cliente, 16",
"localita" : "Loc. del Cliente16",
"telefono" : "+39 0481 9999916",
"email" : "nome16.cognome16@gmail.com",
"updateTime" : "2013-10-04T19:01:19.692+02:00",
"impianti" : [ "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4OIIDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4NILDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4IoJDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4OoKDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4LoIDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4IYLDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4OYJDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4LYKDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4M4IDA", "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4O4LDA" ]
} ]
}
My class getter returns a List of Key-s, so the field "impianti" is the result of List<Key> serialized by my serializer. The Objectify Key class already have a getString() method which returns a String encoded Key rapresentation (i.e. a String like "aglhcHBlbmdpbmVyFQsSCEltcGlhbnRvGICAgICA4OIIDA") and a constructor I use to create a Key from this kind of String.
In the appengine documentation you see the output of the Bar class. Bar is a object so if an instance of Bar class have x=1 and y=2 in JSON you should have:
{"bar": {"x": 1, "y": 2}}
but using the example serializer insted of returning the serialized object {"x": 1, "y": 2} you obtain a simpler
{"bar": "1,2"}
-
7. Re: Re: Custom serializers
alesj Oct 9, 2013 10:54 AM (in response to gregor.sfiligoj)Yeah, I understand all of this.
But what's your return type on the @Api method?
From example, if one would return Bar, it would be illegal,
as Bar --> String, and String cannot be represented as JSON, right?
-
8. Re: Re: Custom serializers
gregor.sfiligoj Oct 9, 2013 1:05 PM (in response to alesj)There is a bit of confusion :-)
JSON IS STRING. JSON rapresent objects as strings.
Appengine do this work for you (using Jackson marshaller). But, it will not serialize returning objects (of methods) which are primitives or enums (they are allowed for input arguments).
Why this limitation is not known to me, because with Jackson, Gson and other JSON marshallers I think that it should be possible.
So, if you would return an Integer you will return a wrapper class instead which has a getter returning Integer instead. For collections Appengine returns a Collection object which has a getItems() getter for example.
But, and here is the point, don't confuse this limitation with the fact that the JSON response IS a string. All responses of REST Http requests are strings if we want say so.
Your custom serializer return a String because that string is embedded in the response. I will try to explain better. Take the example in appengine documentation, the well known Bar class:
public class Bar {
private final int x;
private final int y;
public Bar(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
At some point you have a method that looks like:
public void getBar(@Named("x") Integer x, @Named("y") Integer y) { // "primitives" argument is allowed!
return new Bar(x,y);
}
When GAE serialize the JSON RESPONSE passing x=1 and y=2 in the request will be:
{"x": 1, "y": 2}
This is a JSON rapresentation of Bar object.
Suppose that you are not happy with this rapresentation and instead you prefer this JSON rapresentation:
{"sum":3, "diff":1}
I know that "sum" is x+y and "diff" is y-x so I know also x and y (yes! I'm an engineer :-))
How to obtain this? Custom serializer!
I write something like that:
public class BarSerializer implements Serializer<Bar, String> {
public String serialize(Bar in) {
return "{\"sum\":"+in.getX()+in.getY() + ", \"diff\":" + (in.getY()-in.getX()) + "}";
}
public Bar deserialize(String in) {
int x = ........;
int y = ........;
// some parsing work :-)
return new Bar(x,y);
}
}
Thats'all! In this example I substitute JSON object with another JSON object rapresentation. The original example return a pure string and not a JSON object.
{"bar": {"x": 1, "y": 2}} --> {"bar": "1,2"}
Returning to my project the method which returns the response I posted before is:
@ApiMethod(name = "cliente.list", path = "cliente/{start}/{rows}", httpMethod = HttpMethod.GET)
public ListCliente listCliente(@Named("start") Integer start,
@Named("rows") Integer rows) {
List<Cliente> response = ofy().load().type(Cliente.class).offset(start)
.limit(rows).list();
return new ListCliente(response);
}
ListCliente is a collection wrapper class for List<Cliente>, Cliente is:
public class Cliente {
@Id
Long id;
String nome;
// other properties
Date updateTime;
List<Key> impianti;
// getters/setters
}
My serialzer substitute the "impianti" property serializing my way the Key class.
Uff.... a long response, I hope that is also clear ;-)
-
9. Re: Custom serializers
alesj Dec 27, 2013 7:16 AM (in response to gregor.sfiligoj)Try this http://in.relation.to/Bloggers/FinallyNewCapeDwarfReleaseThisTimeOnWildFly
As I changed how we handle Endpoints -- we now simply use original resources.