Add function to GSON JsonObject - gson

My target is to generate the JSON configuration of an apexchart ( https://apexcharts.com/javascript-chart-demos/bar-charts/custom-datalabels/ ) in Java with GSON.
The configuration contains a property "formatter" that has a JavaScript function as a value:
formatter: function (val, opt) {
return opt.w.globals.labels[opt.dataPointIndex] + ": " + val
},
When I add a property to a JsonObject like this jsonDataLabels.addProperty("formatter", "(val, opt) {...}"); then the property value in the output is (as expected) a String (with quotes) and apexchart doesn't interpret it.
How can I add an unquoted JavaScript function into a GSON JsonObject?

How can I add an unquoted JavaScript function into a GSON JsonObject?
The short answer is you can't; Gson is a JSON library and what you are creating is not JSON but a JavaScript object.
However, you might be able to achieve this with Gson's JsonWriter.jsonValue method. That method is intended to write a raw JSON value, but you could misuse it to write your JavaScript function value:
JsonWriter jsonWriter = new JsonWriter(...);
jsonWriter.beginObject();
jsonWriter.name("formatter");
jsonWriter.jsonValue("function (val, opt) { ... }");
jsonWriter.endObject();
But be careful because jsonValue does not check the syntax of the provided value, so if you provide a malformed value, the resulting JSON (or in your case JavaScript object) might be malformed.
Maybe there are JavaScript code writer libraries out there though, which would be better suited for your use case.

A workaround (not my preferred approach) is ...
to write a unique placeholder into the JSON property,
export the JSON document into a String
replace the unique placeholder within the String

Related

Spring Cloud Function - how to pass multiple parameters to a function call in Kotlin

In the documentation for Spring Cloud Function, the examples for Kotlin consist of function that takes a single parameter, e.g.
#Bean
open fun lower(): (String) -> String = { it.lowercase() }
which is called via a URL that has the single parameter on the end as so:
http://localhost/lower/UpperCaseParam
How can more than one parameter be passed ?
Is something like this supported ?
#Bean
open fun upper(): (String,String) -> String = { x,y -> x+y }
or if not multiple parameters, an object ?
#Bean
open fun upper(): (Pair<String,String>) -> String = { it.first+it.second }
Function by definition has only a single input/output. Even if we were to add support for BiFunction that would only satisfy cases where you have two inputs etc.
The best way to achieve what you want is to use Message Headers which you can pass as HTTP headers.
Then you can make your function signature to accept Function<Message<YourPOJOType>, ...> uppercase(); and then get payload (your main argument, such as request param) and headers from Message.
You can use BiFunction where second argument would be Map representing message headers and first argument payload. This way you can deal with your types and keep your function completely free from anything Spring. BiFunction<YourPOJOType, Map, ...>

Access Spring WebClient response body **BEFORE** being parsed

I've got a problem with an URL call response encoding.
So, just before Spring's WebClient converts the body response into an String object, as desired, I need to access the raw body response to parse it with the proper encoding. So, just before:
<execution>.flatMap(servletResponse -> {
Mono<String> mono = servletResponse.bodyToMono(String.class);
}
I need to access the raw URL call response; I think before "flatMap".
So... I've been looking at "codecs" within Spring documentation, but... So; even for testing, I have:
myWriter is an instance of: EncoderHttpMessageWriter.
myReader is an instance of: DecoderHttpMessageReader.
myWriter handles myDecoder, an instance of Decoder.
myReader handles myEncoder, an instance of Encoder.
as per Spring Documentation about Codecs; and testing with both options for the WebClient Builder:
myWebClientBuilder = WebClient.Builder; // provided by Spring Context,
myWebClientBuilder = WebClient.builder(); // "by hand"
So, the relevant part of code looks like this (tried even with register and registerWithDefaultConfig):
WebClient.builder().codecs(consumer -> {
consumer.customCodecs().register(myWriter.getEncoder());
consumer.customCodecs().register(myWriter);
consumer.customCodecs().register(myReader.getDecoder());
consumer.customCodecs().register(myReader);
})
.build();
shows that the codecs load, and internal basic methods are called:
canDecode
canEncode
canRead
canWrite
getDecodableMimeTypes
getDecoder
getEncodableMimeTypes
getEncoder
but... No one of the read/write... Mono<T> / Flux<T> methods are used. Is there anything left for configuring a codec to properly parse the incoming response with the proper encoding?
The response is a String; a old-fashioned String, with all data-fields in a single row, that wll be parsed later, according to rules about positions and lengths of fields; nothing related with JSON or Jackson.
Is there another better way to perform this pre-action?
Many thanks in advance.

AWS Lex receives Invalid Response from lambda function - Can not construct instance of IntentResponse

Using Java8 in eclipse AWS SDK, I've created and uploaded a lambda function that is hooked in upon fulfillment of my lex intent.
Lambda has not problem receiving JSON request and parsing.
Then, I format a simple "Close" dialogAction response and send back to lex and receive the following error from the Test Bot page in the lex console:
An error has occurred: Received invalid response from Lambda:
Can not construct instance of IntentResponse:
no String-argument constructor/factory method to deserialize
from String value
('{"dialogAction
{"type":"Close","fulfillmentState":"Fulfilled","message":
{"contentType":"PlainText","content":"Thanks I got your info"}}}')
at [Source: "{\"dialogAction\":
{\"type\":\"Close\",\"fulfillmentState\":\"Fulfilled\",\"message\":
{\"contentType\":\"PlainText\",\"content\":\"Thanks I got your
info\"}}}";line: 1, column: 1]
It seems to have a problem right away with the format (line 1, column 1), but my JSON string looks ok to me. Before returning the output string in the handleRequest java function, I am writing the it to the Cloudwatch log and it writes as follows:
{
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": "Thanks I got your info"
}
}
}
Things I've tried:
Removing the message element as it's not required
Adding in non-required properties like sessionAttributes,
responseCard, etc
removing the double quotes
replacing double quotes with single quotes
hardcoding json from sample response format message in documentation
Is there something hidden at the http headers level or is java8 doing something to the JSON that is not visible?
Not sure if this is because I'm using Java8 or not, but a return value of "String" from the RequestHandler class handleRequest method will not work.
Yes, String is an object, but the constructors on the Lex side are expecting an "Object". I was converting my lex response POJO to a String before returning it in the handleRequest method. That was my mistake.
I fixed it by changing the return type of the handleRequest method to be "Object" instead of "String".
public Object handleRequest(Object input, Context context)
instead of
public String handleRequest(Object input, Context context)
You also have to implement the
public class LambdaFunctionHandler implements RequestHandler<Object, Object>
not
public class LambdaFunctionHandler implements RequestHandler<Object, String>
This solved my issue.
In my case I was facing exactly the same issue and was able to fix it by creating specific response POJO type and using this POJO as the return type for 'handleRequest' method. E.g. BotResponse.java as follow:
public class BotResponse implements Serializable{
private static final long serialVersionUID = 1L;
public DialogAction dialogAction = new DialogAction();
public DialogAction getDialogAction() {
return dialogAction;
}
public void setDialogAction(DialogAction dialogAction) {
this.dialogAction = dialogAction;
}
}
Note, I have also added the 'implements Serializable' just to be on safer side. Probably it is an overkill.
Not sure why but for me returning a well formatted JSON String object did not worked even after changing the return type of 'handleRequest' method to 'Object'.
I know this is an old question however thought this might help some else
#Mattbob Solution dint fix my issue, However he is in the right path. Best approach is to use a Response object, a custom response object and make the lambda return the custom response object. So i went to the Documentation and created a custom object that looks Response format here
http://docs.aws.amazon.com/lex/latest/dg/lambda-input-response-format.html
At the time of answering question i couldnt find an object in SDK that matched the response Object so i had to recreate but if some one knows please comment below
Class xxxxx implements RequestHandler<Object, AccountResponse> {
#Override
public AccountResponse handleRequest(Object input, Context context) {
}
}
Lambda will look somewhat like this and just populate and return the object to match response structure and error goes away. Hope this helps.
Whenever we are returning the object to the bot from the backend make sure we need to pass content type along with content. But here we are passing wrong. So wE need to pass as like below. It is in Node.js
let message = {
contentType: "PlainText",
content: 'Testing bot'
};

How to force URIBuilder.path(...) to encode parameters like "%AD"? This method doesn't always encode parameters with percentage, correctly

How to force URIBuilder.path(...) to encode parameters like "%AD"?
The methods path, replacePath and segment of URIBuilder do not always encode parameters with percentage, correctly.
When a parameter contains the character "%" followed by two characters that together form an URL-encoded character, the "%" is not encoded as "%25".
For example
URI uri = UriBuilder.fromUri("https://dummy.com").queryParam("param", "%AD");
String test = uri.build().toString();
"test" is "https://dummy.com?param=%AD"
But it should be "https://dummy.com?param=%25AD" (with the character "%" encoded as "%25")
The method UriBuilderImpl.queryParam(...) behaves like this when the two characters following the "%" are hexadecimal. I.e, the method "com.sun.jersey.api.uri.UriComponent.isHexCharacter(char)" returns true for the characters following the "%".
I think the behavior of UriBuilderImpl is correct because I guess it tries to not encode parameters that already are encoded. But in my scenario, I will never try to create URLs with parameters that already encoded.
What should I do?
My Web application uses Jersey and in many places I build URIs using the class UriBuilder or invoke the method getBaseUriBuilder of UriInfo objects.
I can replace "%" with "%25", every time I invoke the methods queryParam, replaceQueryParam or segment. But I am looking for a less cumbersome solution.
How can I make Jersey to return my own implementation of UriBuilder?
I thought of creating a class that extends UriBuilderImpl that overrides these methods and that perform this replacing before invoking super.queryParam(...) or whatever.
Is there any way of making Jersey to return my own UriBuilder instead of UriBuilderImpl, when invoking UriBuilder.fromURL(...), UriInfo.getBaseUriBuilder(...), etc?
Looking at the method RuntimeDelegate, I thought of extending RuntimeDelegateImpl. My implementation would override the method createUriBuilder(...), which would return my own UriBuilder, instead of UriBuilderImpl.
Then, I would add the file META-INF/services/javax.ws.rs.ext.RuntimeDelegate and in it, a the full class name of my RuntimeDelegateImpl.
The problem is that the jersey-bundle.jar already contains a META-INF/services/javax.ws.rs.ext.RuntimeDelegate that points to com.sun.jersey.server.impl.provider.RuntimeDelegateImpl, so the container loads that file instead of my javax.ws.rs.ext.RuntimeDelegate. Therefore, it does not load my RuntimeDelegateimplementation.
Is it possible to provide my own implementation of RuntimeDelegate?
Should I take a different approach?
UriBuilder
This is possible with help of UriComponent from Jersey or URLEncoder directly from Java:
UriBuilder.fromUri("https://dummy.com")
.queryParam("param",
UriComponent.encode("%AD",
UriComponent.Type.QUERY_PARAM_SPACE_ENCODED))
.build();
Which result in:
https://dummy.com/?param=%25AD
Or:
UriBuilder.fromUri("https://dummy.com")
.queryParam("param", URLEncoder.encode("%AD", "UTF-8"))
.build()
Will result in:
https://dummy.com/?param=%25AD
For a more complex examples (i.e. encoding JSON in query param) this approach is also possible. Let's assume you have a JSON like {"Entity":{"foo":"foo","bar":"bar"}}. When encoded using UriComponent the result for query param would look like:
https://dummy.com/?param=%7B%22Entity%22:%7B%22foo%22:%22foo%22,%22bar%22:%22bar%22%7D%7D
JSON like this could be even injected via #QueryParam into resource field / method param (see JSON in Query Params or How to Inject Custom Java Types via JAX-RS Parameter Annotations).
Which Jersey version do you use? In the tags you mention Jersey 2 but in the RuntimeDelegate section you're using Jersey 1 stuff.
See if the following examples help. The thread linked below has an extensive discussion on the available functions and their differing outputs.
The following:
UriBuilder.fromUri("http://localhost:8080").queryParam("name", "{value}").build("%20");
UriBuilder.fromUri("http://localhost:8080").queryParam("name", "{value}").buildFromEncoded("%20");
UriBuilder.fromUri("http://localhost:8080").replaceQuery("name={value}).build("%20");
UriBuilder.fromUri("http://localhost:8080").replaceQuery("name={value}).buildFromEncoded("%20");
Will output:
http://localhost:8080?name=%2520
http://localhost:8080?name=%20
http://localhost:8080?name=%2520
http://localhost:8080?name=%20
via http://comments.gmane.org/gmane.comp.java.jsr311.user/71
Also, based on the Class UriBuilder documentation, the following example shows how to obtain what you're after.
URI templates are allowed in most components of a URI but their value
is restricted to a particular component. E.g.
UriBuilder.fromPath("{arg1}").build("foo#bar");
would result in encoding of the '#' such that the resulting URI is
"foo%23bar". To create a URI "foo#bar" use
UriBuilder.fromPath("{arg1}").fragment("{arg2}").build("foo", "bar")
instead. URI template names and delimiters are never encoded but their
values are encoded when a URI is built. Template parameter regular
expressions are ignored when building a URI, i.e. no validation is
performed.
It is possible to overwrite the default behavior in jersey manually at start up e.g. with a static helper that calls RuntimeDelegate.setInstance(yourRuntimeDelegateImpl).
So if you want to have an UriBuilder that encodes percents even if they look like they are part of an already encoded sequence, this would look like:
[...]
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.ext.RuntimeDelegate;
import com.sun.jersey.api.uri.UriBuilderImpl;
import com.sun.ws.rs.ext.RuntimeDelegateImpl;
// or for jersey2:
// import org.glassfish.jersey.uri.internal.JerseyUriBuilder;
// import org.glassfish.jersey.internal.RuntimeDelegateImpl;
public class SomeBaseClass {
[...]
// this is the lengthier custom implementation of UriBuilder
// replace this with your own according to your needs
public static class AlwaysPercentEncodingUriBuilder extends UriBuilderImpl {
#Override
public UriBuilder queryParam(String name, Object... values) {
Object[] encValues = new Object[values.length];
for (int i=0; i<values.length; i++) {
String value = values[i].toString(); // TODO: better null check here, like in base class
encValues[i] = percentEncode(value);
}
return super.queryParam(name, encValues);
}
private String percentEncode(String value) {
StringBuilder sb = null;
for (int i=0; i < value.length(); i++) {
char c = value.charAt(i);
// if this condition is is true, the base class will not encode the percent
if (c == '%'
&& i + 2 < value.length()
&& isHexCharacter(value.charAt(i + 1))
&& isHexCharacter(value.charAt(i + 2))) {
if (sb == null) {
sb = new StringBuilder(value.substring(0, i));
}
sb.append("%25");
} else {
if (sb != null) sb.append(c);
}
}
return (sb != null) ? sb.toString() : value;
}
// in jersey2 one can call public UriComponent.isHexCharacter
// but in jersey1 we need to provide this on our own
private static boolean isHexCharacter(char c) {
return ('0' <= c && c <= '9')
|| ('A' <=c && c <= 'F')
|| ('a' <=c && c <= 'f');
}
}
// here starts the code to hook up the implementation
public static class AlwaysPercentEncodingRuntimeDelegateImpl extends RuntimeDelegateImpl {
#Override
public UriBuilder createUriBuilder() {
return new AlwaysPercentEncodingUriBuilder();
}
}
static {
RuntimeDelegate myDelegate = new AlwaysPercentEncodingRuntimeDelegateImpl();
RuntimeDelegate.setInstance(myDelegate);
}
}
Caveat: Of course that way it is not very configurable, and if you do that in some library code that might be reused by others, this might cause some irritation.
For example I had the same problem as the OP when writing a rest client in a Confluence plugin, and ended up with the "manual encode every parameter" solution instead, as the plugins are loaded via OSGi and thus are simply not able to touch the RuntimeDelegateImpl (getting java.lang.ClassNotFoundException: com.sun.ws.rs.ext.RuntimeDelegateImpl at runtime instead).
(And just for the record, in jersey2 this looks very similar; especially the code to hook the custom RuntimeDelegateImpl is the same.)

how to do validation with not well form XML while doing unmarshalling?

I have an unmarshaller along with an MySchema.xsd file.
StreamSource sources = new StreamSource(getClass().getClassLoader().getResourceAsStream("/xmlValidation.xsd"));
SchemaFactory sf = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI );
unmarshaller.setSchema(sf.newSchema(sources));
And make a call to unmarshaller.setEventHandler() function, to specify a custom validation event handler, which basically format a error tips string , by:
final String errorString = new String();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent validationevent) {
if(validationevent.getSeverity()!= ValidationEvent.WARNING){
errorString.format( "Line:Col[" + validationevent.getLocator().getLineNumber()
+ ":" + validationevent.getLocator().getColumnNumber()
+ "]:" + validationevent.getMessage());
return false;
}
return true;
}
});
The above codes seem work ok(I can get java object when the input string is validated. and also the error tips string is formated as excepted)
The problem is that, when the input xml is not well form, it also throw a SaxParseException.
Thanks in advance.
Andrew
Well formed relates to the XML syntax itself, as opposed to being valid WRT an XML schema:
http://en.wikipedia.org/wiki/Well-formed_element
If you have XML that is not well formed then you will get a ValidationEvent.FATAL_ERROR and unmarshalling will not be able to continue, as the underlying parser used by JAXB cannot continue.
For more information:
http://bdoughan.blogspot.com/2010/12/jaxb-and-marshalunmarshal-schema.html
K, I mess up something and get this problem.
Now I figure it out. If I am wrong, please point me out. below it's what I find in javadoc and test on my project:
javax.xml.bind.ValidationEventHandler can handler the constrain error with the given schema constrains, when unmarshaller is unmarshaling.
unmarshaller.unmarshal(xmlInputStream);
The ValidationEventHandler will be called during the unmarshaling process if error occurs.
The SAXEception will be thrown, if the xmlInputStream is not well form.
And I cant find a way to catch the SAXException, throw by the sax parser, so I guess using validation during unmarshaling can't due with un-well form xml string.
I use javax.xml.validation.Validator to validate that the xml string is well form and under constrain.
jaxbValidator.validate(xmlSource);
The above code will throw SAXException.
If no exception is thrown, then unmarshall the xml string into object.

Resources