I am trying to create an Eventlog (ORMSLOG in example), that saves events in human readable form in Datastore.
Doing this should write readable event:
List<Device> devices = ofy().transactionless().load().type(Device.class).list();
ORMSLOG.log(ORMSLOG.GET_ALL_DEVICES, "Devices found: " + String.valueOf(devices));
The ORMSLOG is a simple class.
public class ORMSLOG {
public final static String CREATE_DEVICE = "Create Device";
public final static String GET_ALL_DEVICES = "Get all Devices";
public static void log(final String event, final String data) {
ofy().save().entity(new Event(event, data)).now();
}
}
But the data saved in Datastore is not readable and looks like this:
ORMSLOG data
I need to transform the reference to the object into human readable text.
You are just logging the String representation of the objects, which is done by calling the toString method. Since you did not override the toString method in the Device class, you are getting the pointer to the objects. If you override the toString method in your Device class to return whatever state you want to return, you would see a much better result. Most IDEs (e.g. Eclipse) have an option to generate toString method for you.
Related
I have a mobile app where a user fills out a form, say an Event, and when they save I want to submit the data as JSON to the server. CN1 has the feature to generate JSON easily using PropertyBusinessObject so my Event is defined as follows:
public class Event implements PropertyBusinessObject {
public final Property<Long, Event> eventId = new Property<>("eventId");
public final Property<EventLocation, Event> eventLocation = new Property<>("eventLocation", EventLocation.class);
public final Property<List<EventItinerary>, Event> eventItineraryList = new Property<>("eventItineraryList", XXX);
private final PropertyIndex idx...
}
Both EventLocation and EventItinerary implement PropertyBusinessObject and I've found that when I generate the JSON for Event, EventLocation generates fine but not EventItinerary. When I try and use EventItinerary.class in the "XXX" section above, I get the following error...
Exception: java.lang.ClassCastException - class java.util.ArrayList cannot be cast to class com.codename1.properties.PropertyBusinessObject
which occurs at line 484 of com.codename1.properties.PropertyIndex.toMapRepresentationImpl()
When I use List.class for "XXX" or nothing i.e. new Property<>("eventItineraryList"); then it posts to the server but the JSON contains the name of the class and its memory address i.e.
{
"eventId": 3425567,
"eventLocation" : {
...
},
"eventItineraryList": [
"com.myapp.event.EventItinerary#cdc543c",
"com.myapp.event.EventItinerary#39987ocb",
"com.myapp.event.EventItinerary#cd5t776c",
]
}
My question is what should I put in "XXX" to have the EventItinerary objects have the correct JSON representation?
You need to use a ListProperty so we can traverse into it and the ListProperty should refer to a different PropertyBusinessObject. So this should look roughly like this:
public final ListProperty<EventItinerary, Event> eventItineraryList = new ListProperty<>("eventItineraryList", EventItinerary.class);
Notice the EventItinerary.class which is important. The generic valuegets lost due to erasure. The argument lets us reconstruct the object with the right object types when loading from JSON.'
Also again, for this to work EventItinerary must be a PropertyBusinessObject too.
I found the following use of a wrapper class, and was wondering if it is a good practice or whether its just duplication of code for no reason.
//Class:
public class SomeClass{
public Integer someInt;
public String someString;
}
//Callout Class:
public class CalloutClass{
public SomeClass someMethod(){
//...code to do a callout to an api
SomeClass someClassObj = (SomeClass)JSON.Deserialize(APIResponse.getBody(), SomeClass.class);
return someClassObj;
}
}
//Controller:
public class SomeController {
public SomeController(){
someClassObj = calloutClassObj.someMethod();
SomeWrapper wrapperObj = new SomeWrapper();
for(SomeClass iterObj : someClassObj){
wrapperObj.someWrapperInt = iterObj.someInt;
wrapperObj.someWrapperString = iterObj.someString;
}
}
public class someWrapper{
public Integer someWrapperInt{get;set;}
public String someWrapperString{get;set;}
}
}
The wrapper class "someWrapper" could be eliminated if we just use getters and setters ({get;set;}) in "SomeClass."
Could anyone explain if there could be a reason for following this procedure?
Thanks,
James
My assumption (because, code in controller is extra pseudo) is
SomeClass is a business entity, purpose of which is to store/work with business data. By work I mean using it's values to display it (using wrapper in controller), to calculate smth in other entities or build reports... Such kind of object should be as lightweight as possible. You usually iterate through them. You don't need any methods in such kind of objects. Exception is constructor with parameter(s). You might want to have SomeObject__c as parameter or someWrapper.
someWrapper is a entity to display business entity. As for wrapper classes in controllers. Imagine, that when you display entity on edit page and enter a value for someWrapperInt property, you want to update someWrapperString property (or you can just put validation there, for example, checking if it is really Integer). Usually, as for business entity, you don't want such kind of functionality. But when user create or edit it, you may want smth like this.
I would like to receive these HTTP parameters (POST) in my Resteasy service:
customFields[my_key]=some_value
customFields[my_key2]=some_value2
Something like this doesn't work:
#Form(prefix="customFields")
Map<String, String> customFields
... what happens here is that on the server the new Map is initialized, and the key for the Map entry is set (i.e. "my_key") but value is not set.
Does anyone know how to handle the case like mine, where I need to receive unknown number of fields (within a Map), but each of them properly structured (HTTP map/dictionary notation).
This is a known bug. The workaround is to use your own string wrapper as the map value type. For example:
public class StringWrapper implements Serializable {
private static final long serialVersionUID = 1L;
#FormParam("value")
public String value;
}
Redefine your map as:
#Form(prefix="customFields")
Map<String, StringWrapper> customFields;
And then pass the values to it as customFields[my_key].value=some_value
I have an object which, although it has a text representation (i.e. could be stored in a string of about 1000 printable characters), is expensive to generate. I also have a tree control which shows "summaries" of the objects. I want to drag/drop these objects not only within my own application, but also to other applications that accept CF_TEXT or CF_UNICODETEXT, at which point the textual representation is inserted into the drop target.
I've been thinking of delaying the "rendering" the text representation of my object so that it only takes place when the object is dropped or pasted. However, it seems that Winforms is eagerly calling the GetData() method at the start of the drag, which causes a painful multi-second delay at the start of the drag.
Is there any way ensure that the GetData() happens only at drop time? Alternatively, what is the right mechanism for implementing this deferred drop mechanism in a Winforms program?
After some research, I was able to figure out how to do this without having to implement the COM interface IDataObject (with all of its FORMATETC gunk). I thought it might be of interest to others in the same quandary, so I've written up my solution. If it can be done more cleverly, I'm all eyes/ears!
The System.Windows.Forms.DataObject class has this constructor:
public DataObject(string format, object data)
I was calling it like this:
string expensive = GenerateStringVerySlowly();
var dataObject = new DataObject(
DataFormats.UnicodeText,
expensive);
DoDragDrop(dataObject, DragDropEffects.Copy);
The code above will put the string data into an HGLOBAL during the copy operation. However, you can also call the constructor like this:
string expensive = GenerateStringVerySlowly();
var dataObject = new DataObject(
DataFormats.UnicodeText,
new MemoryStream(Encoding.Unicode.GetBytes(expensive)));
DoDragDrop(dataObject, DragDropEffects.Copy);
Rather than copying the data via an HGLOBAL, this later call has the nice effect of copying the data via a (COM) IStream. Apparently some magic is going on in the .NET interop layer that handles mapping between COM IStream and .NET System.IO.Stream.
All I had to do now was to write a class that deferred the creation of the stream until the very last minute (Lazy object pattern), when the drop target starts calling Length, Read etc. It looks like this: (parts edited for brevity)
public class DeferredStream : Stream
{
private Func<string> generator;
private Stream stm;
public DeferredStream(Func<string> expensiveGenerator)
{
this.generator = expensiveGenerator;
}
private Stream EnsureStream()
{
if (stm == null)
stm = new MemoryStream(Encoding.Unicode.GetBytes(generator()));
return stm;
}
public override long Length
{
get { return EnsureStream().Length; }
}
public override long Position
{
get { return EnsureStream().Position; }
set { EnsureStream().Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return EnsureStream().Read(buffer, offset, count);
}
// Remaining Stream methods elided for brevity.
}
Note that the expensive data is only generated when the EnsureStream method is called for the first time. This doesn't happen until the drop target starts wanting to suck down the data in the IStream. Finally, I changed the calling code to:
var dataObject = new DataObject(
DataFormats.UnicodeText,
new DeferredStream(GenerateStringVerySlowly));
DoDragDrop(dataObject, DragDropEffects.Copy);
This was exactly what I needed to make this work. However, I am relying on the good behaviour of the drop target here. Misbehaving drop targets that eagerly call, say, the Read method, say, will cause the expensive operation to happen earlier.
I'm using app engine datastore so I have entity like this.
#PersistenceCapable
public class Author {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#JsonProperty("id")
#JsonSerialize(using = JsonKeySerializer.class)
#JsonDeserialize(using = JsonKeyDeserializer.class)
private Key key;
....
}
When the model is sent to view, it will serialize the Key object as an Id value. Then, if I send data back from view I want to deserialize the Id back to Key object by using JsonKeyDeserializer class.
public class JsonKeyDeserializer extends JsonDeserializer<Key> {
#Override
public Key deserialize(JsonParser jsonParser, DeserializationContext deserializeContext)
throws IOException, JsonProcessingException {
String id = jsonParser.getText();
if (id.isEmpty()) {
return null;
}
// Here is the problem because I have several entities and I can't fix the Author class in this deserializer like this.
// I want to know what class is being deserialized at runtime.
// return KeyFactory.createKey(Author.class.getSimpleName(), Integer.parseInt(id))
}
}
I tried to debug the value in deserialize's parameters but I can't find the way to get the target deserialized class. How can I solve this?
You may have misunderstood the role of KeySerializer/KeyDeserializer: they are used for Java Map keys, and not as generic identifiers in database sense of term "key".
So you probably would need to use regular JsonSerializer/JsonDeserializer instead.
As to type: it is assumed that handlers are constructed for specific types, and no extra type information is passed during serialization or deserialization process: expected type (if handlers are used for different types) must be passed during construction.
When registering general serializers or deserializers, you can do this when implementing Module, as one of the arguments is type for which (de)serializer is requested.
When defining handlers directly for properties (like when using annotations), this information is available on createContextual() callback of interface ContextualSerializer (and -Deserializer), if your handler implements it: BeanProperty is passed to specify property (in this case field with annotation), and you can access its type. This information needs to be stored to be used during (de)serialization.
EDIT: as author pointed out, I actually misread the question: KeySerializer is the class name, not annotation.