#XStreamOmitField for Restlet GAE not working - java

I have a POJO field annotated with #XStreamOmitField however when I look that the response of the ServerResource, the field is there.
Here is the code I have (simplified):
#Override
public ItemDTO getStuff() {
return stuff.getItem();
}
Here's the POM config I have:
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet</artifactId>
<version>${version.restlet}</version>
</dependency>
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.servlet</artifactId>
<version>${version.restlet}</version>
</dependency>
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.xstream</artifactId>
<version>${version.restlet}</version>
</dependency>
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.json</artifactId>
<version>${version.restlet}</version>
</dependency>
Version is, <version.restlet>2.3.1</version.restlet>
What could the the problem here? It should be automatic right?
Update:
My idea is this is caused by the APISpark library used in my app causing XStream to be not-used in favor of Jackson:
<dependency>
<groupId>org.restlet.gae</groupId>
<artifactId>org.restlet.ext.apispark</artifactId>
<version>${version.restlet}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>

Since the extension org.restlet.ext.apispark uses the extension org.restlet.ext.jackson, the latter automatically registers a converter in addition to the ones from the extensions org.restlet.ext.xstream and org.restlet.ext.json.
The following method allows you to see which converters are registered:
private void configureConverters() {
List<ConverterHelper> converters = Engine.getInstance()
.getRegisteredConverters();
for (ConverterHelper converterHelper : converters) {
System.out.println(converterHelper.getClass());
}
}
#Override
public Restlet createInboundRoot() {
configureConverters();
(...)
}
The registeration depends on the order in the classpath so I guess that the jackson one is registered first. This means that this is the one used to convert the response data of your request.
To be able to use the Jettison extension you need to manually remove the registered Jackson converter, as described below:
private void configureConverters() {
List<ConverterHelper> converters = Engine.getInstance()
.getRegisteredConverters();
JacksonConverter jacksonConverter = null;
for (ConverterHelper converterHelper : converters) {
System.err.println(converterHelper.getClass());
if (converterHelper instanceof JacksonConverter) {
jacksonConverter = (JacksonConverter) converterHelper;
break;
}
}
if (jacksonConverter != null) {
Engine.getInstance()
.getRegisteredConverters().remove(jacksonConverter);
}
}
Hope it helps you,
Thierry

Related

JAXB Marshaller cannot set property for CharacterEscapeHandler

We have a non-Spring application running with JDK 11. In our pom.xml file we have the following:
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
We have code to to a Marshalling of a java object to xml as follows:
Marshaller marshaller = JAXBContext.newInstance(Ratings.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, schemaLocation);
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CDATACharacterEscapeHandler());
StringWriter writer = new StringWriter();
marshaller.marshal(ratings, writer);
return writer.toString();
The code fails when we try to set the CharacterEscapeHandler. We get the following error:
Caused by: javax.xml.bind.PropertyException: name:
com.mycode.server.utils.CDATACharacterEscapeHandler value:
com.mycode.server.utils.CDATACharacterEscapeHandler#74921b67
at javax.xml.bind.helpers.AbstractMarshallerImpl.setProperty
(AbstractMarshallerImpl.java:343)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.setProperty
(MarshallerImpl.java:516)
I can tell you about our custom CDATACharacterEscapeHandler as follows:
// this is the only class that I could find with this name.
// even when I pull in com.sun.xml.bind.jaxb-core 2.3.0
import org.glassfish.jaxb.core.marshaller.CharacterEscapeHandler;
public class CDATACharacterEscapeHandler implements CharacterEscapeHandler {
#Override
public void escape(char[] ch, int start, int length, boolean isAttVal, Writer writer) throws IOException
{
...
}
}
You can see that our CDATACharacterEscapeHandler extends a standard CharacterEscapeHandler.
I've spent about 5 hours research between StackOveflow and the Internet in general. I have been to the JAXB official site. And I know I have tried a lot of things such as:
marshaller.setProperty(CDATACharacterEscapeHandler.class.getName(), new CDATACharacterEscapeHandler());
marshaller.setProperty("com.sun.xml.bind.characterEscapeHandler", new CDATACharacterEscapeHandler());
And nothing seems to work. I one point I tried moving to the latest 3.0.2 versions of code, and that also did not work. It made more of a hassle because the java.xml.bind package was changed to jakarta.xml.bind and I didn't want to update all our code at this time. That can be another effort later.
I have bookmarked other questions on Stackoverflow where someone got answers, and I tried those efforts, but nothing here seemed to work.
On my side I have to change my pom.xml config with:
<!-- JAXB -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<scope>compile</scope>
</dependency>
by changing the original scope of the jaxb-runtime from runtime to compile and then in my code I have something like:
if (cdata) {
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CharacterEscapeHandler() {
#Override
public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException {
// some code here
writer.write(ac, i, j);
}
});
}
and I still have the sonar alert that I am using
import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
in my imports, but I did not found a better way doing it with java >= 11 (currently working in Java 17).
Updating marshaller property key from com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler to org.glassfish.jaxb.characterEscapeHandler may resolve the issue for new JAXB glassfish implementation. Worked for Java 17.
marshaller.setProperty("org.glassfish.jaxb.characterEscapeHandler", new NoEscapeHandler());

Producer#initTransactions doesn't work with KafkaContainer

I try to send messages to Kafka with a transaction. So, I use this code:
try (Producer<Void, String> producer = createProducer(kafkaContainerBootstrapServers)) {
producer.initTransactions();
producer.beginTransaction();
Arrays.stream(messages).forEach(
message -> producer.send(new ProducerRecord<>(KAFKA_INPUT_TOPIC, message)));
producer.commitTransaction();
}
...
private static Producer<Void, String> createProducer(String kafkaContainerBootstrapServers) {
return new KafkaProducer<>(
ImmutableMap.of(
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaContainerBootstrapServers,
ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString(),
ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true,
ProducerConfig.TRANSACTIONAL_ID_CONFIG, UUID.randomUUID().toString()
),
new VoidSerializer(),
new StringSerializer());
}
If I use local Kafka, it works well.
But if I use Kafka TestContainers, it freezes on producer.initTransactions():
private static final String KAFKA_VERSION = "4.1.1";
#Rule
public KafkaContainer kafka = new KafkaContainer(KAFKA_VERSION)
.withEmbeddedZookeeper();
How can I configure KafkaContainer to work with transactions?
Try using Kafka for JUnit instead of Kafka testcontainers. I had the same problem with transactions and made them alive in this way.
Maven dependency that I used:
<dependency>
<groupId>net.mguenther.kafka</groupId>
<artifactId>kafka-junit</artifactId>
<version>2.1.0</version>
<scope>test</scope>
</dependency>
I got an exception using Kafka for JUnit as #AntonLitvinenko suggested. My question about it here.
I added this dependency to fix it (see the issue):
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<version>2.12.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
Also, I used 2.0.1 version for kafka-junit and kafka_2.11:
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>${kafkaVersion}</version>
<scope>test</scope>
</dependency>

Parsing JSON to POJO with LocalDateTime using Jackson "Unexpected token (START_OBJECT), expected VALUE_STRING: Expected array or string."

I have read many postings on here on JSON parsing to Java objects and have had my parsing working just fine until I introduced LocalDateTime. I have tried to using the Java 8 parser, the JSR310 module and to build a customization - below describing the roadblocks on each. Any help would be appreciated!
This is my JSON string, creating by Jackson from another POJO:
{"validEscortsWTheirSpecReqs":"MAYBE",
"modifiedDateTimeNeedToBeThere":
{"dayOfMonth":6,"dayOfWeek":"MONDAY","month":"FEBRUARY","year":2017,
"hour":10,"minute":1,"second":24,"nano":0,"dayOfYear":37,"monthValue":2,
"chronology":{"id":"ISO","calendarType":"iso8601"}
}
}
which generates
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected VALUE_STRING: Expected array or string. at
...
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1376)
at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:118)
at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:39)
...
called from:
ComputeArrive response = mapper.readValue(responseString, ComputeArrive.class);
Response has the following fields:
Logger logger // should not be mapped by JSON, no getters/setters
static final long serialVersionUID = 1L; // "
String validEscortsWTheirSpecReqs = "YES" // want mapped
LocalDateTime modifiedDateTimeNeedToBeThere // want mapped
I have probably registered too many modules by now, adding them in to try to get it to parse:
mapper.findAndRegisterModules();
//mapper .registerModule(new ParameterNamesModule());
mapper.registerModule(new Jdk8Module());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new JSR310Module());
So I have tried to add Spring Boot dependencies to my project and write a customizer for JSR310 (which seems unecessary given the modules above):
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
return objectMapper;
}
}
My application has numerous dependencies so I did not want to make anything a "parent" of it and added the following dependencies. However, the class "Jackson2ObjectMapperBuilder" is not being found so the above does not compiles with these dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
I am not finding a lot of documentation for "Jackson2ObjectMapperBuilderCustomizer" which may be a replacement for "Jackson2ObjectMapperBuilder" - which could be the issue. Please note that the class above is not instantiated by me because I assume Spring instantiates it.
Any thoughts on how to parse my response would be most appreciated!
For me, this problem was solved by adding the JavaTimeModule to the Jackson2ObjectMapperBuilder like this:
#Configuration
public class MyConfiguration {
#Bean
#Primary
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
return builder.modulesToInstall(new JavaTimeModule());
}
}
Then Spring uses this mapper with the registered module.
I think the problem is the parsing of the LocalDate, try this:
{
"validEscortsWTheirSpecReqs":"MAYBE",
"modifiedDateTimeNeedToBeThere":[6,2,2017]
}
day, month, year represented by integers.
As #Tobias said use JavaTimeModule will solve the problem.
Add the JSR310 dependency in your pom xml to allow Jackson to be able to recognize Java 8 Date&Time API data types :
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
You can now use JavaTimeModule in you Jackson configuration like this :
#Configuration
public class JacksonConfig {
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
return objectMapper;
}
}
You should use this configuration to serialize and deserialize LocalDate or other Java 8 Date&Time API data types.

Spring mvc how to return json from controller? [duplicate]

I'm a newbie in the theme. How can I return JSON data from my controller, using something like that (using ResponseBody)
#RequestMapping(value = "/ajaxtest", method = RequestMethod.GET)
#ResponseBody
public Set<String> ajaxTest() {
Set<String> records = new HashSet<String>();
records.add("Record #1");
records.add("Record #2");
return records;
}
I tried ro use Jackson, but have http 406 error.
What correct version of Jackson should I use with Spring version 4.0.3 and what is the algoritm of using?
UPD
Ajax call
<button id="btn">Click!</button>
<script>
$("#btn").click(
function sendAjax() {
$.ajax({
url: "/ajaxtest",
dataType: "json",
success: function(data) {
alert(data);
},
error:function() {
alert("error");
}
});
})
</script>
For Converting to json request you must include following 3 jar to your project build path.
Jackson jar is use for convrting HTTP request to jason format.
Also mention headear=content-type=application/json
These are the jar files
jackson-mapper-asl.jar
jackson-core-asl.jar
jackson-jaxrs.jar
If you are using maven, you can include the following dependency in your pom.xml
Jackson Mapper Version 1 which is good enough to convert your object to JSON object:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Or you can also go for latest Jackson version,
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.0</version>
</dependency>

Unable to find unambiguous method: class com.fasterxml.jackson.databind.node.ArrayNode.get(java.lang.Long)

I have a Spring MVC project with Maven managing dependencies. I need to read the JSON and display its content to the view.
Given a simple JSON object
{
"items" : [{"model" : "m1"}, {"model" : "m2"}, {"model" : "m3"}]
}
I leverage packages from Jackson Project to read and parse the file, and then set the value in #Controller
JsonNode itemsNode = Node.path("items");
model.addAttribute("items", itemsNode);
On the JSP, I retrieve the values
Item 0: ${items.get(0)}, Item 1: ${items.get(1)}, Item 2: ${items.get(2)}
The problem I encountered is,
everything works as expected when I use
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
but I got error,
HTTP Status 500 - javax.el.MethodNotFoundException: Unable to find unambiguous method: class com.fasterxml.jackson.databind.node.ArrayNode.get(java.lang.Long)
when I replaced both <dependency> to
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
with no source code changes(except import statements). Spring is 4.1.5.RELEASE
${items.get(0)}
The JSP is treating the 0 as a Long, but ArrayNode.get() takes an int. Check out the answer to this question for more details. In short, you could try this:
${items.get( (0).intValue() )}

Categories