Reading a byte array from couchbase in Spring - spring

I have a simple Object that I am trying to read/write into couchbase (using spring data).
Here is the object:
#Document
public class CacheObject {
#Id
private String id;
private byte[] data;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
}
I try to read/write it using the Couchbase template:
#Test
public void test2() throws Exception {
CacheObject o = new CacheObject();
o.setId("test1");
o.setData("test123".getBytes());
CouchbaseTemplate t = c.couchbaseTemplate();
t.save(o);
CacheObject o2 = t.findById("test1", CacheObject.class);
System.out.println("COOL " + new String(o2.getData()));
}
The template comes from a config that extended AbstractCouchbaseConfiguration.
The write works fine, I see the base64 encoded value in couchbase.
The read throws an Exception:
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type byte for value 'd293'; nested exception is java.lang.NumberFormatException: For input string: "d293"
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:189)
at org.springframework.core.convert.support.StringToArrayConverter.convert(StringToArrayConverter.java:63)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:35)
...
I can get around this by using a custom reader (This is in my config that extended AbstractCouchbaseConfiguration). Using this code, everything works.
public CustomConversions customConversions() {
return new CustomConversions(Arrays.asList(StringToByteConverter.INSTANCE));
}
#ReadingConverter
public static enum StringToByteConverter implements Converter<String, byte[]> {
INSTANCE;
#Override
public byte[] convert(String source) {
return Base64.decodeBase64(source);
}
}
Am I doing something wrong?
I have tried 1.2.2 and 1.3.0.M1 and both give the same results.
Thanks

I think you are doing this correctly. If you observe the json that got stored in Couchbase, it looks like this:
{
"_class": "CacheObject",
"data": "dGVzdDEyMw=="
}
When you try to convert the data property back to a CacheObject, it is attempting to convert the string above to a primitive byte array, hence the NumberFormatException. Is there something you are trying to accomplish by storing a byte array the way you are doing it?
According to the approach you are testing, I think you need a custom converter to transform the string version of the byte array as expected, because couchbase template doesn't support this type by default. See this link for supported types:
http://docs.couchbase.com/developer/dev-guide-3.0/using-json-docs.html
Hope this helps.

Related

How to implement the custom converter for a field using Spring Data Elasticsearch?

I need to implement a custom converter in spring data elastic-search. I need to concatenate something to the text when saving and retrieving. I have seen a similar question is here. And in the answer is says it is implemented now and available. But I didn't found any example how to implement it. So anybody know how to do this?
You can find examples in the test code of the library.
You have to create a converter:
class FooConverter implements PropertyValueConverter {
public static final String PREFIX = "foo-";
#Override
public Object write(Object value) {
return PREFIX + value.toString();
}
#Override
public Object read(Object value) {
String valueString = value.toString();
if (valueString.startsWith(PREFIX)) {
return valueString.substring(PREFIX.length());
} else {
return value;
}
}
}
and then register it for the property of your entity class:
#Document(indexName = "foo")
class Entity {
#Field(type = FieldType.Text)
#ValueConverter(FooConverter.class)
private String someField;
// ...
}

JSON-B serializes Map keys using toString and not with registered Adapter

I have a JAX-RS service that returns a Map<Artifact, String> and I have registered a
public class ArtifactAdapter implements JsonbAdapter<Artifact, String>
which a see hit when deserializing the in-parameter but not when serializing the return value, instead the Artifact toString() is used. If I change the return type to a Artifact, the adapter is called. I was under the impression that the Map would be serialized with built-in ways and then the adapter would be called for the Artifact.
What would be the workaround? Register an Adapter for the whole Map?
I dumped the thread stack in my toString and it confirms my suspicions
at java.lang.Thread.dumpStack(Thread.java:1336)
Artifact.toString(Artifact.java:154)
at java.lang.String.valueOf(String.java:2994)
at org.eclipse.yasson.internal.serializer.MapSerializer.serializeInternal(MapSerializer.java:41)
at org.eclipse.yasson.internal.serializer.MapSerializer.serializeInternal(MapSerializer.java:30)
at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:63)
at org.eclipse.yasson.internal.Marshaller.serializeRoot(Marshaller.java:118)
at org.eclipse.yasson.internal.Marshaller.marshall(Marshaller.java:74)
at org.eclipse.yasson.internal.JsonBinding.toJson(JsonBinding.java:98)
is the serializer hell-bent on using toString at this point?
I tried
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class PersonAdapter implements JsonbAdapter{
#Override
public String adaptToJson(Person obj) throws Exception {
return obj.getName();
}
#Override
public Person adaptFromJson(String obj) throws Exception {
return new Person(obj);
}
}
public class Test {
public static void main(String[] args) {
Map<Person, Integer> data = new HashMap<>();
data.put(new Person("John"), 23);
JsonbConfig config = new JsonbConfig().withAdapters(new PersonAdapter());
Jsonb jsonb = JsonbBuilder.create(config);
System.out.println(jsonb.toJson(data, new HashMap<Person, Integer>() {
}.getClass().getGenericSuperclass()));
}
}
but still ended up with the toString() of Person
Thanks in advance,
Nik
https://github.com/eclipse-ee4j/yasson/issues/110 (in my case since that's the default provider for WildFly)

How to convert Oracle user defined Type into java object in spring jdbc stored procedure

I am working on springjdbcTemplate, and all db call will be done through stored procedures. In Oracle 11g I have created one user defined type containing with other type as field inside it as below.
create or replace type WORKER AS Object (NAME VARCHAR2(30),
age NUMBER);
create or replace type WORKER_LIST IS TABLE OF WORKER;
create or replace type MANAGER AS Object(
NAME VARCHAR2(30),
workers WORKER_LIST
);
And at Java side I have created the classes as follows.
public class Worker implements SQLData {
private String name;
private int age;
#Override
public String getSQLTypeName() throws SQLException {
return "WORKER";
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
setAge(stream.readInt());
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getName());
stream.writeInt(getAge());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Manager implements SQLData {
private String name;
private List<Worker> workers;
#Override
public String getSQLTypeName() throws SQLException {
return "Manager";
}
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
setWorkers((List<Worker>) stream.readObject());
}
#Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getName());
stream.writeObject((SQLData) getWorkers());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Worker> getWorkers() {
return workers;
}
public void setWorkers(List<Worker> workers) {
this.workers = workers;
}
}
I have mentioned in typeMap about the mappings.
But I am not getting expected results.
Worker type is returned as Struct and List<Worker> is returned as array.
Please let me know what should I have do and what is the standard protocol to get the expected object as I mentioned above. I'm new to JDBCTemplate. Please suggest.
Thanks
Ram
I think I've managed to get something working.
You mentioned something about the connection's type map. When using Spring it's difficult to get hold of the database connection in order to add the types to the connection's type map, so I'm not sure what you mean when you write 'I have mentioned in typeMap about the mappings'.
Spring offers one way to add an entry to the connection's type map, in the form of the class SqlReturnSqlData. This can be used to call a stored procedure or function which returns a user-defined type. It adds an entry to the connection's type map to specify the database type of the object and the class to map this object to just before it retrieves a value from a CallableStatement. However, this only works if you only need to map a single type. You have two such types that need mapping: MANAGER and WORKER.
Fortunately, it's not difficult to come up with a replacement for SqlReturnSqlData that can add more than one entry to the connection's type map:
import org.springframework.jdbc.core.SqlReturnType;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
public class SqlReturnSqlDataWithAuxiliaryTypes implements SqlReturnType {
private Class<?> targetClass;
private Map<String, Class<?>> auxiliaryTypes;
public SqlReturnSqlDataWithAuxiliaryTypes(Class<?> targetClass, Map<String, Class<?>> auxiliaryTypes) {
this.targetClass = targetClass;
this.auxiliaryTypes = auxiliaryTypes;
}
#Override
public Object getTypeValue(CallableStatement cs, int paramIndex, int sqlType, String typeName) throws SQLException {
Connection con = cs.getConnection();
Map<String, Class<?>> typeMap = con.getTypeMap();
typeMap.put(typeName, this.targetClass);
typeMap.putAll(auxiliaryTypes);
Object o = cs.getObject(paramIndex);
return o;
}
}
The above has been adapted from the source of SqlReturnSqlData. All I've really done is added an extra field auxiliaryTypes, the contents of which gets added into the connection's type map in the call to getTypeValue().
I also needed to adjust the readSQL method of your Manager class. The object you read back from the stream will be an implementation of java.sql.Array. You can't just cast this to a list. Sadly, getting this out is a little fiddly:
#Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
Array array = (Array) stream.readObject();
Object[] objects = (Object[]) array.getArray();
List<Worker> workers = Arrays.stream(objects).map(o -> (Worker)o).collect(toList());
setWorkers(workers);
}
(If you're not using Java 8, replace the line with Arrays.stream(...) with a loop.)
To test this I wrote a short stored function to return a MANAGER object:
CREATE OR REPLACE FUNCTION f_get_manager
RETURN manager
AS
BEGIN
RETURN manager('Big Boss Man', worker_list(worker('Bill', 40), worker('Fred', 36)));
END;
/
The code to call this stored function was then as follows:
Map<String, Class<?>> auxiliaryTypes = Collections.singletonMap("WORKER", Worker.class);
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("my_schema")
.withFunctionName("f_get_manager")
.declareParameters(
new SqlOutParameter(
"return",
OracleTypes.STRUCT,
"MANAGER",
new SqlReturnSqlDataWithAuxiliaryTypes(Manager.class, auxiliaryTypes)));
Manager manager = jdbcCall.executeFunction(Manager.class);
// ... do something with manager.
This worked, in that it returned a Manager object with two Workers in it.
Finally, if you have stored procedures that save a Manager object to the database, be aware that your Manager class's writeSQL method will not work. Unless you've written your own List implementation, List<Worker> cannot be casted to SQLData. Instead, you'll need to create an Oracle array object and put the entries in that. That however is awkward because you'll need the database connection to create the array, but that won't be available in the writeSQL method. See this question for one possible solution.

How to set different type for variable in POJO than expected while deserializing json using gson in android(see example)

Bear with my English. I have a simple json,
{
"Hint2": "L"
}
this is the POJO that works.
public class Hints {
#SerializedName("Hint2")
#Expose
private String Hint2;
public void setHint1(Object Hint2) {
this.Hint2 = (Hint2);
}
}
i want to change it to
public class Hints {
#SerializedName("Hint2")
#Expose
public final ObservableField<String> Hint2 = new ObservableField<>();
public void setHint2(String Hint2) {
this.Hint2.set(Hint2);
}
}
both class has same setter method, same #SerializedName annotation tag. only type of Hint2 object is changed. but the latter one throws exception shown below
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at..
so i believe deserialization depends on what kind of variable "Hint2" is.
Is there a way to make it work with ObservableField rather than using String?
The reason i'm trying this is android binding library, which supports binding objects directly to xml files. and the ObservableField automatically updates UI when corresponding value in POJO is changed.
Update:
gson design document has this
Using fields vs getters to indicate Json elements
Some Json libraries use the getters of a type to deduce the Json elements. We chose to use all fields (up the inheritance hierarchy) that are not transient, static, or synthetic. We did this because not all classes are written with suitably named getters. Moreover, getXXX or isXXX might be semantic rather than indicating properties.
However, there are good arguments to support properties as well. We intend to enhance Gson in a latter version to support properties as an alternate mapping for indicating Json fields. For now, Gson is fields-based.
so this indicates that Gson is fields-based. this pretty much answers my question but still waiting if anyone has someway around this.
I came across the same requirements, and resolved it finally, here are the steps:
create the class GsonUtils:
public class GsonUtils {
// code following
//...
}
following code are in this class
write a customized serializer & deserializer:
private static class ObservableFieldSerializerDeserializer implements JsonSerializer>, JsonDeserializer> {
#Override
public JsonElement serialize(ObservableField src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.get());
}
#Override
public ObservableField deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final Type type = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
return new ObservableField((T) GsonUtils.getGson().fromJson(json, type));
}
}
you need to register ObservableField types to Gson:
private static GsonBuilder createGsonBuilder() {
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken&ltObservableField&ltString&gt&gt(){}.getType(), new ObservableFieldSerializerDeserializer&ltString&gt());
...// register more types which are wrapped by ObservableFields
return gsonBuilder;
}
create the Gson which is used by the deserializer
private static final Gson sGson = createGson();
private static Gson createGson() {
return createGsonBuilder().create();
}
// this is used by the deserializer
public static Gson getGson() {
return sGson;
}
that's all, hope it helps
I just ran into what I think is the same issue, and here is a JUnit4 test showing how I solved it with Jackson for a POJO, but of course String would work as well.
public class ObservableDeserializationTest {
private static class ObservableDeserializer extends JsonDeserializer<ObservableField> implements ContextualDeserializer {
private Class<?> mTargetClass;
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
mTargetClass = property.getType().containedType(0).getRawClass();
return this;
}
#Override
public ObservableField deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObservableField result = new ObservableField();
result.set(p.readValueAs(mTargetClass));
return result;
}
}
private static class SomePojo {
public String id;
public String name;
}
private static class ObservableTestClass {
#JsonDeserialize(using = ObservableDeserializer.class)
public ObservableField<SomePojo> testObj = new ObservableField<>();
}
#Test
public void DeserializingAnObservableObjectShouldSetValueCorrectly() {
ObservableTestClass tc = null;
try {
tc = new ObjectMapper().readValue("{\"testObj\":{\"name\":\"TestName\",\"id\":\"TestId\"}}", ObservableTestClass.class);
} catch (IOException e) {
e.printStackTrace();
}
Assert.assertEquals("TestName", tc.testObj.get().name);
Assert.assertEquals("TestId", tc.testObj.get().id);
}
}
The key is the ContextualDeserializer interface that allows extracting the contained class type. Jackson provides several options for registering a custom deserializer, so this is but one way of doing it. Also, it would probably be a good idea to override getNullValue as well in the deserializer if you would use this for real.

Why it seems impossible to use BeanUtils.copyProperties from a JPA entity to a JAX-B Bean?

We are using JPA Entities to get the database rows and then when we transfer that to the external, we want to use disconnected object (DTO) which are simple beans annotated with JAX-B.
We use a mapper and its code looks like this:
public BillDTO map(BillEntity source, BillDTO target) {
BeanUtils.copyProperties(source, target);
return target;
}
But when the code is running we get an error like this:
java.lang.IllegalArgumentException: argument type mismatch
Note this is the Spring implementation of the BeanUtils:
import org.springframework.beans.BeanUtils
And the naming of the properties are identical (with their getter/setter).
Anybody knows why the error happens?
And how to use a fast way instead just copying properties one by one?
This example working well. Here String property is copied to enum property:
Entity:
public class A {
private String valueFrom;
public String getValue() {
return valueFrom;
}
public void setValue(String value) {
this.valueFrom = value;
}
}
DTO (En is enumeration):
public class B {
private En valueTo;
public void setValue(String def) {
this.valueTo = En.valueOf(def);
}
public void setEnumValue(En enumVal) {
this.valueTo = enumVal;
}
}
As for your GitHub example, problem in class B in getter should be:
public String getValue()
Example:
public String getValue() {
return value.toString();
}

Resources