I am writing a process that needs to pull JSON data from an API and provide it to another system that requires the field names to be completely lowercased. I have attempted to utilize the built in LowerCaseStrategy but this does not work. An example of what I have tried is:
package com.example
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
public class Example {
private static final ObjectMapper mapper = new ObjectMapper();
public Example(){
mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.LowerCaseStrategy());
}
public JsonNode fetchData(String url) throws MalformedURLException, IOException{
JsonNode data = mapper.readTree(new URL(url));
return data;
}
}
A PropertyNamingStrategy affects how JSON property names are mapped from methods and fields in a Java class. This code doesn't do any mapping to Java objects, it only deserializes JSON into a JsonNode. In that case, PropertyNamingStrategy doesn't apply, and the names are retained from the original JSON source.
I was able to find the following gist which is working for me https://gist.github.com/stmcallister/92d0b4c2355a490ffed008cfbda69063
There's probably a better solution out there but this is perfect for my use case.
Related
How to set representation for specific format on Json Serializer used by MicroProfile Rest Client?
I have a service that requires a year-month input, formated as ISO 8601 "YYYY-MM". The server-side is working, and the response is correctly formatted, but implementation is Resteasy.
I'm using MicroProfile Rest Client, and entity attribute is defined as java.util.YearMonth.
The request is sent with serialized JSON year-month attribute represented as an object when I want it to be a formatted string.
Already tried annotating attribute with JsonFormat, JsonbDateFormat and #Schema, with same results.
Also tried adding Jackson Provider (ContextResolver), which works for the server-side, but no changes on the client-side.
Code snippet of testes annotations:
import com.fasterxml.jackson.annotation.JsonFormat;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import javax.json.bind.annotation.JsonbDateFormat;
#JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM")
#JsonbDateFormat(value = "yyyy-MM")
// Next one I'm guessing. Can't find docs for this.
#Schema(type = SchemaType.STRING, implementation = YearMonth.class, pattern = "yyyy-MM")
private YearMonth referencia;
Environment:
Wildfly 21
Java SE 11
microprofile-rest-client-api:2.0
microprofile-openapi-api:2.0
1. First, you need to enable Jackson, as RESTEasy defaults to JSONB. For this you need to add this system property:
resteasy.preferJacksonOverJsonB = true
Add system property as your preference, using -Dproperty=value command line syntax or whatever. In our case, we use WildFly configuration (e.g. standalone.xml):
<system-properties>
<property name="resteasy.preferJacksonOverJsonB" value="true"/>
</system-properties>
2. Then you can add Jackson configuration provider to your REST client interface:
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
#RegisterProvider(JacksonConfiguration.class)
#RegisterRestClient
public interface RestTestService {
// methods...
}
Now, Java 8 Date Time API will correctly be serialized, and you can add custom formats with #JsonFormat, if needed.
Plus: Here a sample of a Jackson configuration provider that can be used on client and server-side too:
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
#Provider
public class JacksonConfiguration implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public JacksonConfiguration() {
mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
My company has a Java web service using Spring Web that accepts JSON via a REST API. We're using Maven and our Jackson version is 2.9. We're trying to prevent deserialization exceptions from being thrown when an integrator passes in an empty list when our API isn't expecting one.
For example, here's my application class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#EnableWebMvc
#SpringBootApplication
public class ExampleApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
Student.java class:
import lombok.Data;
import java.util.Map;
#Data
public class Student {
private String firstName;
private String lastName;
private Map<String, String> classGrades;
}
A StudentController:
package com.example.example.controllers;
import com.example.example.models.Student;
import org.springframework.http.RequestEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.stream.Stream;
#RestController()
#RequestMapping(value = "/Students/", produces = "application/json")
public class StudentController {
#PostMapping(path = "")
public RequestEntity<Student> createNewStudent(#RequestBody Student student) {
return null;
}
}
The application.properties:
spring.jackson.deserialization.accept-empty-array-as-null-object=true
The pom.xml contains all the default dependencies, with the following added:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
Every other project file is default to the project structure generated by the Spring initializer. The expected request body (JSON formed using Postman):
{
"firstName": "Mark",
"lastName": "Twain",
"classGrades": {
}
}
Works just fine. However, if any field (though in our specific case, the classGrades field) receives an empty list, a Jackson deserialization exception is thrown. An example JSON request that fails:
{
"firstName": "Mark",
"lastName": "Twain",
"classGrades": []
}
And the exception that is thrown:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token
at [Source: (PushbackInputStream); line: 65, column: 28]
According to the project's github page, the option ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT should resolve this issue for us. We've tried setting this directly on an ObjectMapper configuration object and within application.properties using the line:
spring.jackson.deserialization.accept-empty-array-as-null-object=true
Neither method seemed to take effect. We're currently using a workaround using the #JsonDeserialize(using = MyCustomDeserializer.class) annotaion on fields prone to this problem. However, we would like to be able to have all of our fields treat empty lists as null by default.
Are we misunderstanding the configuration option and using it incorrectly? Is there a way to treat empty lists as null within our app, and if so, how can we accomplish this?
For this example case, my problem was the #EnableWebMvc annotation within the ExampleApplication class. Removing that annotation allowed me to successfully send an empty array to my endpoint, which then received it as a null object.
Note
My original problem still exists within my company's application, even after removing the annotation. However, it seems like this may be an issue with a different setting that might be clashing with ...accept-empty-arrays-as-null-object.
A Map is serialized as
{ "k1": "v1", "k2": "v2"}
An empty Map is serialized as
{ }
In your example you try to put a [], which represents an empty list or array, into a Map and that is why Jackson complains. Simply, cannot map an empty list into a Map.
This also explains why enabling ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT has no effect
Update
I was not right. Jackson can map empty array to a Map.
Created a minimal springboot example that uses the spring.jackson.deserialization.accept-empty-array-as-null-object=true property and can map an empty array [] to the Map
Check here if you want.
I am rewriting an old REST service written in an in-house framework to use Spring. I have a Controller with a POST method which takes a parameter either as a POST or as x-www-form-urlencoded body. Following multiple StackOverflow answers, I used #ModelAttribute annotation and created a model.
My problem is that the old REST API is using a property name in snake case - say some_property. I want my Java code to follow the Java naming conventions so in my model the field is called someProperty. I tried using the #JsonProperty annotation as I do in my DTO objects but this time this didn't work. I only managed to make the code work if the field in the model was named some_property. Here is my example code:
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
#RestController
#RequestMapping("/my/api/root")
public class SomethingController {
#PostMapping("/my/api/suffix")
public Mono<Object> getSomething(
#RequestParam(name = "some_property", required = false) String someProperty,
#ModelAttribute("some_property") Model somePropertyModel) {
// calling my service here
}
public class Model {
#JsonProperty("some_property")
private String someProperty;
private String some_property;
// Getters and setters here
}
}
I am searching for annotation or any other elegant way to keep the Java naming style in the code but use the legacy property name from the REST API.
The #JsonProperty annotation can only work with the JSON format, but you're using x-www-form-urlencoded.
If you can't change your POST type, you have to write your own Jackson ObjectMapper:
#JsonProperty not working for Content-Type : application/x-www-form-urlencoded
I also met a similar case you,
Please replace #ModelAttribute("some_property") with #RequestBody.
Hope to help you!
The Situation
I have an enum class called Errors and it is in a common project. The errors enum contains an int code and a String text. The Errors enum is used in two different projects. One project is a Spring based project and the other project is in a non-Spring J2EE application.
In the Spring based project, Jackson is the library used for converting the Errors enum to Json. So if there is a request we can return an Errors enum object and it will be automatically converted to the json representation, which is {code: int, text: error} The Errors enum has the annotation:
#JsonSerialize(using=ErrorsSerializer.class)
The ErrorsSerializer class is located in the Spring based application.
In the common project, where the Errors enum is located, Gson is the library used for converting objects to its Json representation and we want to remove any references to the Jackson library.
The problem
If i remove the annotation from the Errors enum the Spring project will not return the correct representation of the Errors enum, it will only return the constant variable in quotes.
The Question
How do i remove the annotation of the Errors enum (which will remove any Jackson dependencies in the common project) and yet still have the Spring project return the correct Json representation?
Best Option So Far
The best option i have come up with so far is to create an Errors container object in the Spring application that contains the Errors enum and also has the json serialize annotation.
Thanks!
You can also specify serializer in Spring based project using Module functionality. Module allow users to customize ObjectMapper object without annotations. See below example:
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class JacksonProgram {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(Error.class, new ErrorJsonSerializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(Error.NOT_FOUND));
}
}
Above example prints:
{"code":1,"text":"Not found"}
See below links to find solution how to configure ObjectMapper in Spring app:
Register a custom Jackson ObjectMapper using Spring Javaconfig.
Configurating ObjectMapper in Spring.
I have an XML file, like
<stock><name>AXL</name><time>19-07</time><price>11.34</price></stock>
<stock><name>AIK</name><time>19-07</time><price>13.54</price></stock>
<stock><name>ALO</name><time>19-07</time><price>16.32</price></stock>
<stock><name>APO</name><time>19-07</time><price>13.56</price></stock>
...............more
How can I parse this into JSON structure file?
For a simple solution, I recommend Jackson, a Java library for generating and reading JSON with an extension for XML, as it can transform arbitrarily complex XML into JSON with just a few simple lines of code.
input.xml
<entries>
<stock><name>AXL</name><time>19-07</time><price>11.34</price></stock>
<stock><name>AIK</name><time>19-07</time><price>13.54</price></stock>
<stock><name>ALO</name><time>19-07</time><price>16.32</price></stock>
<stock><name>APO</name><time>19-07</time><price>13.56</price></stock>
</entries>
The Java Code:
import java.io.File;
import java.util.List;
import org.codehaus.jackson.map.ObjectMapper;
import com.fasterxml.jackson.xml.XmlMapper;
public class Foo
{
public static void main(String[] args) throws Exception
{
XmlMapper xmlMapper = new XmlMapper();
List entries = xmlMapper.readValue(new File("input.xml"), List.class);
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writeValueAsString(entries);
System.out.println(json);
// [{"name":"AXL","time":"19-07","price":"11.34"},{"name":"AIK","time":"19-07","price":"13.54"},{"name":"ALO","time":"19-07","price":"16.32"},{"name":"APO","time":"19-07","price":"13.56"}]
}
}
This demo uses Jackson 1.7.7 (the newer 1.7.8 should also work), Jackson XML Databind 0.5.3 (not yet compatible with Jackson 1.8), and Stax2 3.1.1.
http://keithchadwick.wordpress.com/2009/03/14/converting-xml-to-json-with-xsl-part-2/
You haven't specified language... so... I haven't got more specific on it than to think "you already have XML, probably you have access to xsl/xslt":
http://www.w3.org/TR/xslt
http://www.thomasfrank.se/xml_to_json.html