Storing multidimensional arrays with Morphia - unmarshalling

I have troubles reading/unmarshalling multidimensional arrays with Morphia.
The following class:
#Entity
class A {
double[][] matrix;
}
is properly marshalled and stored in mongodb, but when reading it I get an exception that the double[][] can not be constructed. I've tried to use a custom TypeConverter but it is not getting invoked for such types.
Similar issues I get when using a member like this:
List<double[]> matrix;
I did not find any annotations that could help morphia figure out what type is expected in the array.
I suspect this is not supported yet.
Any suggestions ?
Thanks in advance.

I haven't used multi-dimensional arrays with Morphia yet, so I can't say much about that.
However, I've done the following for unsupported data types (like BigDecimal):
Define the unsupported data type as transient
Define a supported data type for storing your information
Serialize / unserialize it into a supported data type via #PrePersist and #PostLoad
My code looks something like this:
#Transient
private BigDecimal salary;
private String salaryString;
#PrePersist
public void prePersist(){
if(salary != null){
this.salary = this.salary.setScale(2, BigDecimal.ROUND_HALF_UP);
salaryString = this.salary.toString();
}
}
#PostLoad
public void postLoad(){
if(salary != null){
this.salary = this.salary.setScale(2, BigDecimal.ROUND_HALF_UP);
this.salary = new BigDecimal(salaryString);
}
}

Related

Is there a JPA annotation equivalent of jackson's #JsonAnyGetter/#JsonAnySetter?

With jackson, I can use #JsonAnyGetter and #JsonAnySetter to serialize/deserialize a Map<String, Object> into extra fields of a json object. Is there a JPA annotation that will do similar things with extra db column values being get/set from/into a member Map?
Specifically, I'd like to use jooq's .fetchInto(Pojo.class) to hydrate a java object. I can manually use .fetch(RecordMapper<Record, Pojo>) to get the results I want by hydrating the Map member from the Record fields manually, but wondering if there's a more automatic way of doing this. Pojo code could look something like the following (use lombok's #Data to make it concise):
#Data
public class Pojo {
#Column("field1")
private int field1;
#Column("field2")
private String field2;
#JsonAnyGetter // works for json serialization,
#JsonAnySetter // is there an equivalent for JPA?
private Map<String, Object> extraFields;
}
You can register a RecordMapperProvider with your jOOQ configuration in order to override how various methods, including fetchInto(Class) apply mapping:
https://www.jooq.org/doc/3.11/manual/sql-execution/fetching/pojos-with-recordmapper-provider/

mapStruct: map list to other list?

I have a list List<Payment> which I'd like to map to another list List<PaymentPlan>. These types look like this:
public class Payment {
#XmlElement(name = "Installment")
#JsonProperty("Installment")
private List<Installment> installments = new ArrayList<>();
#XmlElement(name = "OriginalAmount")
#JsonProperty("OriginalAmount")
private BigDecimal originalAmount;
//getters setters, more attributes
}
and....
public class PaymentPlan {
//(Installment in different package)
private List<Installment> installments;
#XmlElement(name = "OriginalAmount")
#JsonProperty("OriginalAmount")
private BigDecimal originalAmount;
//getters setters, more attributes
}
I expect that something like this is working...
#Mappings({
#Mapping(//other mappings...),
#Mapping(source = "payments", target = "paymentInformation.paymentPlans")
})
ResultResponse originalResponseToResultResponse(OrigResponse originalResponse);
...but I get:
Can't map property java.util.List<Payment> to java.util.List<PaymentPlan>.
Consider to declare/implement a mapping method java.util.List<PaymentPlan> map(java.util.List<Payment> value);
I don't know how to apply this information. First I though I need to declare some extra mapping (in the same mapper class) for the lists, so MapStruct knows how to map each field of the List types like this:
#Mappings({
#Mapping(source = "payment.originalAmount", target = "paymentInformation.paymentPlan.originalAmount")
})
List<PaymentPlan> paymentToPaymentPlan(List<Payment> payment);
...but I get error messages like
The type of parameter "payment" has no property named "originalAmount".
Obviously I do something completely wrong, since it sound like it does not even recognize the types of the List.
How can I basically map from one List to another similar List? Obviously I somehow need to combine different mapping strategies.
btw: I know how to do it with expression mapping, like...
#Mapping(target = "paymentPlans",expression="java(Helper.mapManually(payments))")
but I guess MapStruct can handle this by iself.
I presume you are using version 1.1.0.Final. Your extra mapping is correct, the only difference is that you need to define a mapping without the lists MapStruct will then use that to do the mapping (the example message is a bit misleading for collections).
PaymentPlan paymentToPaymentPlan(Payment payment);
You don't even need the #Mappings as they would be automatically mapped. You might also need to define methods for the Instalment (as they are in different packages).
If you switch to 1.2.0.CR2 then MapStruct can automatically generate the methods for you.

GWT AutoBean - not serializing ArrayList<String>, other pojos ok?

I can't get an ArrayList to serialize using the AutoBean mechanism. I'm using GWT 2.7. Here is my setup:
public interface IUser {
String getName();
void setName(String name);
ArrayList<String> getFriends();
void setFriends(ArrayList<String> friends);
}
public class User implements IUser {
private String name;
private ArrayList<String> friends;
... getters and setters for all members attributes ...
}
then my bean factory:
public interface AutoBeanFactoryImpl extends AutoBeanFactory {
AutoBean<IUser> user(IUser inst);
}
and finally my usage:
ArrayList<String> friends = new ArrayList<String>();
friends.add("Mary");
User user = new User();
user.setName("Fritz");
user.setFriends(friends);
AutoBeanFactoryImpl factory = GWT.create(AutoBeanFactoryImpl.class);
AutoBean<IUser> bean = factory.user(user);
String json = AutoBeanCodex.encode(bean).getPayload();
The output json does not have the friends array. It has the name ok though. Why doesn't the string array get serialized? Do I need something special for that?
Thanks
The problem is that you specified ArrayList instead of just plain List - in order to nicely generate code that fills in the gaps between Java and JSON, autobeans only want to work with interfaces. Aside from getters and setters, AutoBeans have special support for List, Set (which is just a List without duplicates in the JSON), and Map. If you specify a particular implementation, the AutoBean tooling can't always handle it.
Feel free to pass in an ArrayList instance, but declare your getter and setter to be of type List.
As an aside, those collections can be of any type that AutoBeans can otherwise handle - including numbers, booleans, strings, dates, and any other bean-like interface that also conform to what AutoBeans can deal with.

Can't insert new entry into deserialized AutoBean Map

When i try to insert a new entry to a deserialized Map instance i get no exception but the Map is not modified. This EntryPoint code probes it. I'm doing anything wrong?
public class Test2 implements EntryPoint {
public interface SomeProxy {
Map<String, List<Integer>> getStringKeyMap();
void setStringKeyMap(Map<String, List<Integer>> value);
}
public interface BeanFactory extends AutoBeanFactory {
BeanFactory INSTANCE = GWT.create(BeanFactory.class);
AutoBean<SomeProxy> someProxy();
}
#Override
public void onModuleLoad() {
SomeProxy proxy = BeanFactory.INSTANCE.someProxy().as();
proxy.setStringKeyMap(new HashMap<String, List<Integer>>());
proxy.getStringKeyMap().put("k1", new ArrayList<Integer>());
proxy.getStringKeyMap().put("k2", new ArrayList<Integer>());
String payload = AutoBeanCodex.encode(AutoBeanUtils.getAutoBean(proxy)).toString();
proxy = AutoBeanCodex.decode(BeanFactory.INSTANCE, SomeProxy.class, payload).as();
// insert a new entry into a deserialized map
proxy.getStringKeyMap().put("k3", new ArrayList<Integer>());
System.out.println(proxy.getStringKeyMap().keySet()); // the keySet is [k1, k2] :-( ¿where is k3?
}
}
Shouldn't AutoBeanCodex.encode(AutoBeanUtils.getAutoBean(proxy)).toString(); be getPayLoad()
I'll check the code later, and I don't know if that is causing the issue. But it did stand out as different from my typical approach.
Collection classes such as java.util.Set and java.util.List are tricky because they operate in terms of Object instances. To make collections serializable, you should specify the particular type of objects they are expected to contain through normal type parameters (for example, Map<Foo,Bar> rather than just Map). If you use raw collections or maps you will get bloated code and be vulnerable to denial of service attacks.
Font: http://www.gwtproject.org/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes

NullPointerException when using GWT's AutoBean deserialization with HashMap

I have some problem with the Google's AutoBean serialization and deserialization.
I have an AutoBean that contains primitive types and Maps as well. I can serialize and deserialize the primitive types without any problem, but when i try to read the deserialized Map, i get NullPointerException.
Have you ever met with a similar problem before? There is a JUnit test that representes my problem. The first two asserts are passes, but the third fails.
public class AutoBeanTest {
#Test
public void test() throws Exception {
MyFactory myFactory = AutoBeanFactorySource.create(MyFactory.class);
Options options = myFactory.options().as();
options.setMyInt(5);
HashMap<Double, Boolean> map = newHashMap();
map.put(8.0, true);
map.put(9.1, false);
options.setMyMap(map);
Options deserialized = AutoBeanCodex.decode(myFactory, Options.class, AutoBeanCodex.encode(AutoBeanUtils.getAutoBean(options)).getPayload()).as();
assertEquals(deserialized.getMyInt(),5);
assertTrue(options.getMyMap().containsKey(8d));
assertTrue(deserialized.getMyMap().containsKey(8d));
}
public interface MyFactory extends AutoBeanFactory {
AutoBean<Options> options();
}
public interface Options {
public int getMyInt();
void setMyInt(int myInt);
Map<Double, Boolean> getMyMap();
void setMyMap(Map<Double, Boolean> myMap);
}
}
I've been playing around with the AutoBean functionality a while ago. I think it is still kind a buggy. I'm quite sure the exceptions is caused by a bug in the AutoBean code, not in your code.
If you run the above sample code in a debugger and check the generated JSON, things look fine. You can even call deserialized.getMyMap().size() and get the correct value, but once you want to access the content errors occur.
There is a workaround, just use Map<String, String> instead of Double or Boolean and it works...
Ackchyually... Autobeans is doing it correctly as in JSON only strings are allowed as keys. But of course the error message should be more helpful.