How to display Blob /Json data field in Liferay 7.2? - json

I am using a service builder that is retrieving the form data fine from mysql db. I have a field that has the json data and I tried to map it using object mapper and using com.fasterxml.jackson.databind.ObjectMapper to display the json content. However, the Blob Data is shown as: com.mysql.cj.jdbc.Blob#4ca74f7f
How do I actually get/extract the data from the storing link above? Here is my code snippet:
for (ddmcontent MyTemp : myList) {
System.out.println("Content ID : "+myList.getContentId());
System.out.println("User Blob Data : "+myList.getData());
Blob responseBody =myList.getData();
ObjectMapper objectMapper = new ObjectMapper();
List<ModelData> myDat = objectMapper.readValue((DataInput)
responseBody,objectMapper.getTypeFactory().constructCollectionType
(List.class,ModelData.class));
for (ModelData dt : myDat) {
System.out.println("User Name : "+dt.Name);
System.out.println("Users Email : "+dt.Email);
}
}
Please note, I have defined my ModelData elements as all String.
Any suggestion? What am I missing?
Thanks in advance!

The toString() representation hints at the object's type com.mysql.cj.jdbc.Blob. If you look up its javadoc (or the interface it implements) you'll see the options that you have to decode the contents of the Blob, namely getting an InputStream or a byte[] representation, which you'd have to subject to the correct character set decoding to turn it into a String.
Make sure you nail the character set by testing it with all kinds of Unicode content, so that you don't have to fix badly encoded database content later when your table contains a lot of data in unknown encodings.
As you're using Liferay's Service Builder, you might want to share the relevant parts of your service.xml (optional model-hints.xml) to check for an easier implementation.

I finally got this working by changing the field in question to String and addining a max length to certain number of Char
Thanks!

Related

Create list of custom objects from flat JSON data

I want to create an arraylist of type Adapter from a JSON. But since the JSON is not in arraylist format, I'm unable to use gson.fromJson() method.
Is there any way by which I can create a list of my custom object by parsing the following JSON?
JSON data:
"source":{"adapter-config.adapter[0].name":"testAdapter1",
"adapter-config.adapter[0].resolverName":"serviceResolver",
"adapter-config.adapter[0].parameters[0].key":"serviceId",
"adapter-config.adapter[0].parameters[0].value":"serviceIdPathInEvent",
"adapter-config.adapter[0].parameters[1].key":"appId",
"adapter-config.adapter[0].parameters[1].value":"appIdPathEvent",
"adapter-config.adapter[0].parameters[2].key":"env",
"adapter-config.adapter[0].parameters[2].value":"envPathInEvnet"}
My Adapter Object:
public class Adapter {
private String name;
private String resolverName;
private List<KeyValuePair<String, String>> attributeList;
}
Gson does not provide such functionality out of the box. However you can achieve this by manually reading the JSON data from a JsonReader, consuming the JSON property names with nextName() and then parsing them to determine which data they represent. You could either directly read from a JsonReader, or in case the shown JSON data is only an extract from a larger JSON document, you can implement a TypeAdapter for your List<Adapter>. That TypeAdapter could then either be registered with a GsonBuilder by providing new TypeToken<List<Adapter>>() {}.getType() as type, or you could annotate the field holding the List<Adapter> with #JsonAdapter.
For the actual parsing of List<Adapter>, I would recommend storing a current adapter (and its index in the list) in a local variable. Whenever you parse a JSON property name, you could then check if the index encoded in the name is equal to the index of the current adapter, then you are going to modify the existing instance, otherwise if the encoded index is equal to the index of the current adapter + 1 you create a new Adapter instance, add it to the list of adapters and reassign the current adapter variable and its index variable. Then you continue with parsing the remainder of the property name to find out which Adapter field values to set.
(In case you get stuck there, feel free to let me know in the comments and I can try to provide some concrete code; but it would probably be best if you tried it yourself first.)

Google Protobuf - missing descriptor for type registry

I have some Google Protobuf json with an '#type' field whose value is an URI that will tell someone the type of Protobuf message it is e.g. "#type": "type.googleapis.com/llcore.ModemDataAtlas". From my understanding, these json strings should be serialized into an 'Any' type first. whenever I do this though, I get the following error:
System.InvalidOperationException: Type registry has no descriptor for type name ''llcore.ModemMessage'
Here is my code:
string jsonString = "";
string jsonPath = #"C:\Users\user\Documents\TestRepos\ExampleProto\protoType.json";
using (StreamReader reader = new StreamReader(jsonPath))
{
jsonString = reader.ReadToEnd();
}
Any anyProto = Any.Parser.ParseJson(msg); // Exception occurs here
Does anyone know how to properly deserialize into a Protobuf Any class? How do I go about creating a type registry and adding a descriptor for it?
First let me explain why you cannot simply parse/serialize arbitrary JSON to Any (or vice versa). Any itself has 2 fields:
typeUrl - In your case "type.googleapis.com/llcore.ModemDataAtlas"
value - The actual content encoded as a binary protobuf
A binary protobuf encodes each field as a pair of key and content, think of it like a Map. The key is not the name of the field, rather the number given to the field in the proto definition.
A JSON Object is a Map too, but it uses the field name as index. So, without any type information (Descriptor) for the type the JSON parser would not know how to map a named field of a JSON object, to a binary encoded version of it that is only identified by a number.
Just to make it clear: You cannot parse/serialize unknown types that are encoded as Any! Your app needs to know about them, either by statically compiling the descriptors into it or by looking up type descriptors at runtime. This is basically the point of the TypeRegistry to allow you to load descriptors at runtime, but you have to provide this infrastructure of looking up types yourself.
To make this work in your case, your app needs to know about the types that are packed in the Any. Then you can create a TypeRegistry and pass that to the JsonParser constructor. I'm not a C# developer, but something along the lines:
var registry = TypeRegistry.FromMessages(MyMessage.Descriptor);
var parser = new JsonParser(new JsonParser.Settings(20, registry));
var message = parser.Parse<Any>(payload);

JSON object requires curly braces?

I have an JSF application that stores/retrieves data to/from Oracle DB. Very simple application. I used Gson (from google) API to convert String type data to JSON format into a table in DB.
When I inspected stored data from the table, I expected "{" and "}" beginning and end but I didn't see it instead data was wrapped with double quotes. Is this correct?
Here is an example.
From UI (.xhtml) there is an inputText field to capture data. The data is part (property) of JPA entity model and this model is persisted to DB.
(foo = foo)
gson.toJson(variable_that_holds_above_value);
After querying data from the DB, data stored as ... The column that contains below data is clob type.
"(foo \u003d foo)"
"=" is encoded as "\u003d" b/c I didn't use disableHtmlEscaping().
[update]
I realized that gson.toJson() does what it should but what I wanted/expected was "{"keyName":"foo=foo"}. I naively thought simply providing a string to toJson() will do that for me.
I decided to use JsonObject instead. Reading this How to convert a String to JsonObject using gson library helped me.
JsonObject jb = new JsonObject();
jb.addProperty("keyName","foo=foo");

JSON string to byte[] JSONParseException

I've gotten a whole bunch of angularJS, rest and entity stuff working together to be able to save form data via REST in my POSTGRES DB.
However, I decided to try something different with one of the entity fields so that I can store longer bits of text in the DB. I wanted to use an Entity with a field as follows:
#Column
private int age;
#Lob
#Column(length = 2147483647)
private byte[] otherNeeds;
Now the entity was created just fine. I built my REST interface on top of that based from JBoss Forge scaffolding. Everything works fine with my saving of the form via REST as long as I keep the "otherNeeds" field empty.
Here's my form:
<input id="age" type="number" ng-model="primaryGuest.age"></input>
<input id="otherNeeds" type="text" ng-maxlength="2147483647" ng-model="primaryGuest.otherNeeds"></input>
All this data is passed through correctly into my JavaScript objects in AngularJS, and when I click the save() button, I can see the POST request go thru to my REST interface with the following payload:
{
otherNeeds: "jjjjjj"
age: "10"
}
However, the response back from the server is an exception:
Caused by: org.codehaus.jackson.JsonParseException: Failed to decode VALUE_STRING as base64 (MIME-NO-LINEFEEDS): Illegal character '"' (code 0x22) in base64 content
at [Source: org.apache.catalina.connector.CoyoteInputStream#1a37992c; line: 1, column: 67]
at org.codehaus.jackson.JsonParser._constructError(JsonParser.java:1433) [jackson-core-asl-1.9.9-redhat-2.jar:1.9.9-redhat-2]
at org.codehaus.jackson.impl.Utf8StreamParser.getBinaryValue(Utf8StreamParser.java:402) [jackson-core-asl-1.9.9-redhat-2.jar:1.9.9-redhat-2]
at org.codehaus.jackson.map.deser.std.PrimitiveArrayDeserializers$ByteDeser.deserialize(PrimitiveArrayDeserializers.java:289)
at org.codehaus.jackson.map.deser.std.PrimitiveArrayDeserializers$ByteDeser.deserialize(PrimitiveArrayDeserializers.java:275)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:299)
at org.codehaus.jackson.map.deser.SettableBeanProperty$MethodProperty.deserializeAndSet(SettableBeanProperty.java:414)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:697)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2704)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1315)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:419)
at org.jboss.resteasy.core.interception.MessageBodyReaderContextImpl.proceed(MessageBodyReaderContextImpl.java:105) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.read(GZIPDecodingInterceptor.java:63) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
at org.jboss.resteasy.core.interception.MessageBodyReaderContextImpl.proceed(MessageBodyReaderContextImpl.java:108) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:169) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
... 33 more
So, I'm not sure how to handle this scenario to have my object be transformed into something that would be acceptable for the byte[] format. I have the option of going back to a standard "String" field but I want to be able to provide the user with an "unlimited" amount of text that they can provide.
Any suggestions would be much appreciated.
I'm not exactly sure what the issue was with the #Lob type column and why it didn't want to push the data into the DB; however, eventually, I changed from
#Lob
to
#Type(type="org.hibernate.type.StringClobType")
private String otherNeeds;
And then I was able to manage this more straightforwardly and push content into the DB via the REST calls.
When the property type is byte[] and the response is in String jackson thinks the data is base64 encoded and tries to decode it and convert it to byte[]. But when you changed the type to String no such conversion is taking place and the string is directly saved in database.

Accept only number formats for a property from JSON

I am developing ASP.Net Web API application and we are exposing a REST API for different clients. I have a problem when taking users' JSON files and converting them to Data Model classes. My JSON looks like below.
{"engagementid":1,"clientname":"fsdfs","myno":"23,45","address1":"fsd","address2":"fsdfs","city":"fsdfs","zip":"fsdf","info":"fsdfs","country":"fsdfs","currency":"NOK"}
You can see that my "myno" is sent as a string. But in my Server Data Model "myno" is a double value. So what happen here is when I send the value for "myno" as "23,45", it gets assigned to MyNo property of my Model as 2345. This is wrong, because you can see that the number has been changed because of this wrong conversion. What I simply need is to restrict this conversion. I mean, I want to send an error to Client if he sends a string for "myno" property. Since it is a double value in my Server Data Model, I want to accept only numbers from the client for this property. Which means, I want it like this.
{"myno":2345} //correct
{"myno":"2345"} //wrong. I want to send a error to user by saying, "We only accept Numbers for this value"
How do I do this?
Update:
This problem gets solved if I am using int in my server-model. I mean, if a client send a string to a property which is represented as int in my model, then it gives an error to user by saying string to int conversion can not be done.
I don't whether it is correct or not. I am just telling my suggestion according to my experience. Why can't you create a custom validation attribute and check the datatype of data.
public class IsNumberAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if(value.GetType() != Int)
return new ValidationResult("Only Numbers Allowed");
return null;
}
}