I've been scanning stack overflow all day and have not come across a viable solution for my problem.
I have a pojo that has primitive types and nested objects. For example...
#JsonIgnoreProperties({"duration", "errorCode", "haveFieldsChanged",
"serviceRequestToken", "storedProcDuration"}) // Abstract Base Class
properties
class Bus extends AbstractBaseClass implements Serializable{
#JsonIgnore
private static final long serialVersionId = 1;
#JsonProperty("name")
String name;
#JsonProperty("id")
int id;
#JsonProperty("students")
List<Student> students; // Nested Objects
#JsonProperty("employer")
Employer employer; //Nested object
// Getters and setters - none are annotated
#JsonRootName(value = "student")
class Student implements Serializable{
// student fields
}
#JsonRootName(value = "statusType")
class Employer implements Serializable{
#JsonProperty("id")
int id;
}
When I serialize my Bus object, jackson has no problem creating the proper structure for name, id, and my list of students. However, it will skip over Employer entirely leaving it absent from the json. See below.
{
"name":"Sean",
"id": 1,
"students":[student objects...]
}
I have tried #JsonProperty, #JsonSerialize(as = Employer.class), I tried building map for the employer object. I feel like I have exhausted most options. Is there something I am missing?
I ran into stack overflow exceptions trying some other annotations. I appreciate any help I can get.
The reason I added #JsonProperty to the fields is to help drive deserialization. I think this may be the root cause for serialization, but I am not certain.
Serialization Implementation
private String serializeBus(Bus bus) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
String json = null;
try {
json = mapper.writeValueAsString(bus);
} catch (JsonProcessingException e) {
logger.error("Error serializing bus");
throw new Exception(e);
}
return json;
}
Thank you!
So, if it is not feasible for you to share code, then I put your example above in a little sandbox project: https://github.com/mle-enso/stackoverflow
It currently runs with Spring Boot 2.1 but also downgrading the managed Jackson version(s) to 2.6.1 has no negative impact.
Maybe you could try this project with a simple mvn clean verify or a manual test run of de.mle.stackoverflow.jackson.BusSerializerTest to dig further into this issue here.
I'm building REST service on Jersey and using Jackson to produce JSON from java classes of my model. Model with absolutely simple values, I think this is the most typical case. But I get strange result:
[{\"name\":\"Nick\",\"role\":\"admin\",\"age\":\"32\",\"rating\":47}]
My expecting result:
[{"name":"Nick","role":"admin","age":"32","rating":47}]
My source values of fields does NOT contains any special characters. These are simple words.
There're my Java classes.
Entity:
public class User {
private String name;
private String role;
private String age;
private Integer rating;
Class of rest service:
#ServiceConfig(contextName = "myContext")
#Path("/myrest")
public class MyRestService {
private static final String JSON_CONTENT_TYPE = MediaType.APPLICATION_JSON + ";charset=UTF-8";
#Context
protected HttpServletResponse response;
#GET
#Path("/users")
#OpenTransaction
#Produces({MediaType.APPLICATION_JSON})
public String findUsers(#QueryParam("department") String department) {
response.setContentType(JSON_CONTENT_TYPE);
PDTResponse.status(response).sendStatus(Response.Status.OK.getStatusCode());
List<User> users = new ArrayList<>();
users.add(new User("Nick", "admin", "32", 47));
String jsonInString;
ObjectMapper mapper = new ObjectMapper();
try {
jsonInString = mapper.writeValueAsString(users);
} catch (JsonProcessingException ex) {
jsonInString = "thrown exception: " + ex.getMessage();
}
return jsonInString;
}
I've tried to use annotation #JsonRawValue for string properties:
#JsonRawValue
private String name;
But result in this case was:
[{\"name\":Nick,\"role\":admin,\"age\":32,\"rating\":47}]
And I expect:
[{"name":"Nick","role":"admin","age":"32","rating":47}]
It's obvious that Jackson somehow escapes the quotes in result json of response. But why does it do it, and most importantly how to avoid that? By themselves they are just strings! Without any quotes or special characters.
I use Java 7 and Jackson 2.6.1. And Postman to test result.
Any ideas for fix of my problem?
You can configure the ObjectMapper:
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
String jsonUsers = mapper.writeValueAsString(users);
more info here
All strings in java have to escape quotes in them. So jsonInString should have slashes in it. When you output jsonInString though it shouldn't have the quotes. Are you looking at it in a debugger or something?
Do this.
ObjectMapper mapper = new ObjectMapper();
mapper.getFactory().setCharacterEscapes(new JsonUtil().new CustomCharacterEscapes());
ObjectWriter writer = mapper.writer();
String jsonDataObject = mapper.writeValueAsString(configMap);
public class CustomCharacterEscapes extends CharacterEscapes {
private final int[] _asciiEscapes;
public CustomCharacterEscapes() {
_asciiEscapes = standardAsciiEscapesForJSON();
//By default the ascii Escape table in jackson has " added as escape string
//overwriting that here.
_asciiEscapes['"'] = CharacterEscapes.ESCAPE_NONE;
}
#Override
public int[] getEscapeCodesForAscii() {
return _asciiEscapes;
}
#Override
public SerializableString getEscapeSequence(int i) {
return null;
}
}
If you are using Spring and the #ControllerAdvice for JSONP, then create a wrapper for the JSON string and use #JsonRawValue on the property. The JSONP #ControllerAdvice will not wrap a String response, it needs an Object.
public class JsonStringResponse {
#JsonValue
#JsonRawValue
private String value;
public JsonStringResponse(String value) {
this.value = value;
}
}
#GetMapping
public ResponseEntity<JsonStringResponse> getJson() {
String json = "{"id":2}";
return ResponseEntity.ok().body(new JsonStringResponse(json));
}
#ControllerAdvice
public class JsonpControllerAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpControllerAdvice() {
super("callback");
}
}
Response is a json object {"id":2}
If there is a callback parameter the response is callbackparameter({"id":2});
Looks like you are over complicating your JAX-RS resource class.
To use Jackson as a JSON provider for Jersey 2.x, you don't need to create an ObjectMapper instance like that. There's a better way to achieve it. Keep reading for more details.
Adding Jackson module dependencies
To use Jackson 2.x as your JSON provider you need to add jersey-media-json-jackson module to your pom.xml file:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.25.1</version>
</dependency>
Registering the Jackson module
Then register the JacksonFeature in your Application / ResourceConfig subclass:
#ApplicationPath("/api")
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(JacksonFeature.class);
return classes;
}
}
#ApplicationPath("/api")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(JacksonFeature.class);
}
}
If you don't have an Application / ResourceConfig subclass, you can register the JacksonFeature in your web.xml deployment descriptor. The specific resource, provider and feature fully-qualified class names can be provided in a comma-separated value of jersey.config.server.provider.classnames initialization parameter.
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.jackson.JacksonFeature</param-value>
</init-param>
The MessageBodyWriter provided by Jackson is JacksonJsonProvider. For more details on how to use Jackson as a JSON provider, have a look at this answer. If you need to customize the ObjectMapper, refer to this answer.
Fixing your resource class
By using the approach described above, you resource class can be as simple as:
#Path("/users")
public class MyRestService {
#GET
#Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8"})
public List<User> findUsers() {
List<User> users = new ArrayList<>();
users.add(new User("Nick", "admin", "32", 47));
return Response.ok(users).build();
}
When requesting such endpoint, it will give you the expected JSON as result.
I have also the same problem and tried different solutions, but non works. The problem is not with the mapper, but with the input to the mapper. As in your case:
jsonInString = mapper.writeValueAsString(users); 'users' is a collection. You need to convert each user to JSONObject, add it to JSONArray and then use the mapper on the array: like this
JSONArray users = new JSONArray();
for (Collection user : usersCollection) {
JSONObject user = new JSONObject(mapper.writeValueAsString(user));
users.put(user);
}
mapper.writeValueAsString(user));
I don't know why, but in my case it works doing this :
private static final String COOKIE_TEMPLATE = "{0}={1};Version={2};Domain={3};Max-Age={4};Path='/'";
response.addHeader("Set-Cookie", MessageFormat.format(COOKIE_TEMPLATE, cookie.getName(),cookie.getValue(), cookie.getVersion(), cookie.getDomain(),Integer.toString(cookie.getMaxAge())));
return ResponseEntity.ok(...);
cookie is a javax.servlet.http.Cookie, and cookie.getValue() contains a string produced by
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(obj);
If I use
response.addCookie(cookie)
I have a resulting cookie definition as JSON with backslashes.
But, if I use
response.addHeader("Set-Cookie",MessageFormat(TEMPLATE,cookie.get...))
I managed the same resulting cookie definition as JSON, but without backslashes.
In case of having several cookies, addHeader("Set-Cookie") only creates/updates the desired cookie. The other ones are maintained and won't be altered.
public class StateDate{
#JsonRawValue
Boolean state;
#JsonRawValue
String date;
public String toJson() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false);
try {
return mapper.writeValueAsString(this);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
I've faced similar issue, Following configuration will help sort the issue:
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, false);
For some people who still need an answer if struggling
Try adding #JsonRawValue to the field.
The #JsonRawValue annotation can instruct Jackson to serialize a property exactly as is.
Even I came across this problem today and I stumbled across this question. People have provided multiple ways of removing the backslashes, but the thing is that the problem goes down to the very essence of what we are trying to do here.
We want to return the json response of an api call, but we are returning it as a JSONString formatted in way so that it can be printed, read and understood in Java. When you print it it looks exactly the way you want it to be when you return it.
Long story short, you must return the bytes from the function, not a String. Change the return type to byte[] and return this:
new ObjectMapper().writeValueAsString(response).getBytes(StandardCharset.UTF_8);
This will give you the purest JSON you ever want to read. Mostly, people face this issue when on the other side they are reading from an InputStream and are unable to map it to the same class and it does not work. This is how you'll fix it.
It should not be a problem, just you need to parse it in javascript and use it : JSON.parse(response)
I'm currently working on a SpringBoot API to interface with a MongoRepository, but I'm having trouble understanding how the JSON being passed becomes a Document for storage within Mongo. I currently have a simple API that stores a group of users:
#Document
#JsonInclude
public class Group {
#Id
#JsonView(Views.Public.class)
private String id;
#JsonView(Views.Public.class)
private String name;
#JsonView(Views.Public.class)
private Set<GroupMember> groupMembers = new HashSet<>();
}
There are also setter and getter methods for each of the fields, although I don't know how necessary those are either (see questions at the end).
Here is the straightforward component I'm using:
#Component
#Path("/groups")
#Api(value = "/groups", description = "Group REST")
public class Groups {
#Autowired
private GroupService groupService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get all Groups", response = Group.class, responseContainer = "List")
#JsonView(Views.Public.class)
public List<Group> getAllGroups() {
return groupService.getAllGroups();
}
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Create a Group", response = Group.class)
#JsonView(Views.Detailed.class)
public Group submitGroup(Group group) {
return groupService.addGroup(group);
}
}
Finally, I have a Service class:
#Service
public class GroupServiceImpl implements GroupService {
#Autowired
private GroupRepository groupRepository;
#Override
public Group addGroup(Group group) {
group.setId(null);
return groupRepository.save(group);
}
#Override
public List<Group> getAllGroups() {
return groupRepository.findAll();
}
}
The GroupRespository is simply an interface which extends MongoRepository<Group,String>
Now, when I actually make a call to the POST method, with a body containing:
{
"name": "group001",
"groupMembers": []
}
I see that it properly inserts this group with a random Mongo UUID. However, if I try to insert GroupMember objects inside the list, I receive a null pointer exception. From this, I have two questions:
How does SpringBoot (Jackson?) know which fields to deserialize from the JSON being passed? I tested this after deleting the getter and setter methods, and it still works.
How does SpringBoot handle nested objects, such as the Set inside the class? I tested with List instead of Set, and it worked, but I have no idea why. My guess is that for each object that is both declared in my class and listed in my JSON object, SpringBoot is calling a constructor that it magically created behind the scenes, and one doesn't exist for the Set interface.
Suppose I'm adamant on using Set (the same user shouldn't show up twice anyway). What tools can I use to get SpringBoot to work as expected?
It seems to me that a lot of the things that happen in Spring are very behind-the-scenes, which makes it difficult for me to understand why things work when they do. Not knowing why things work makes it difficult to construct things from scratch, which makes it feel as though I'm hacking together a project rather than actually engineering one. So my last question is something like, is there a guide that explains the wiring behind the scenes?
Finally, this is my first time working with Spring... so please excuse me if my questions are entirely off the mark, but I would appreciate any answers nonetheless.
I'm trying to serialize an object in Java using Jackson, but when I'm trying to serialize it, it gives me this error:
No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer
I tried this post, but it didn't help.
Here is the class I'm trying to serialize:
public class Repository {
public String name;
#JsonIgnore // to avoid recursive calls
public ArrayList<UserRole> contributors = new ArrayList<UserRole>();
public User self;
public ArrayList<FileInfo> files;
public RepositoryType repositoryType;
public String path;
}
I also tried to create getters/setters for each field but still nothing.
Here is my serialization method:
public static String convertObjectToJson(Object object) throws IOException {
ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = objectWriter.writeValueAsString(object); //error on this line
return json;
}
Looks like your one of your classes has java.io.FileDescriptor reference.
By default, Jackson will only work with with fields that are either public, or have a public getter methods – serializing an entity that has all fields private or package private will fail
If you look at the source code of java.io.FileDescriptor you can see
there are private fields without public getters.
You should configure your objectMapper visibility to allow access to private fields also.
// For jackson 2.*
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
// For jackson lower than 2
objectMapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
I was facing problems to send objects to Thymeleaf template with ResponseEntity it was giving me exception "StackOverFlowError" while serializing and your note " #JsonIgnore // to avoid recursive calls" solved my problem. Thanks
i'm using Jersey to build a REST service and as Json Processor i set Jackson in my application.
#javax.ws.rs.ApplicationPath("/")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("controller");
register(JacksonFeature.class);
}
I implement a ContextResolver for Jacksons ObjectMapper (as it's suggested in this post Configure Jersey/Jackson to NOT use #XmlElement field annotation for JSON field naming) which creates an ObjectMapper that doesn't fail on unknown properties during deserialization:
#Provider
public class MyJsonObjectMapperProvider implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(Class<?> type)
{
System.out.println("mapper!!!");
ObjectMapper result = new ObjectMapper();
result.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return result;
}
}
and then i register this class in my application inserting register(MyJsonObjectMapperProvider.class) in the class MyApplication shown above. I obtain what i want, in sense that if there are unknown properties in the json the object mapper doesn't fail.
My problem is another; i have this class that i use to map a specified Json, in order to deserialize it and subsequently serialize it:
public class Version {
private String status;
private String updated;
private String id;
private List<Link> links;
#XmlElement(name = "media-types")
private List<MediaTypes> media_types;
//constructor + getter and setter
}
The problem is about the element media_types and the use of the annotation #XmlElement. Before i insert the ContextResolver to personalize ObjectMapper all works fine, in fact after serialization i obtain a json in which the element/attribute media_types has as name media-types; on the contrary with ContextResolver this element doesn't change it's name and has media_types. I think that, during serialization, the annotation XmlElement doesn't work, but i'm not sure that this is the correct reason.
Another attempt i try to do is to put #JsonProperty("media-types") annotation instead of #XmlElement annotation but with no result; in fact with this annotation i obtain also a Processing Exception.
The last attempt (in addition to what has been suggested by the previous post) was that of insert these lines of code in the ContextResolver:
AnnotationIntrospector intr = new AnnotationIntrospector.Pair(new JaxbAnnotationIntrospector(),new JacksonAnnotationIntrospector());
// usually we use same introspector(s) for both serialization and deserialization:
result.getDeserializationConfig().withAnnotationIntrospector(intr);
result.getSerializationConfig().withAnnotationIntrospector(intr);
in order to use both JaxbAnnotation and JacksonAnnotation but the name of the field in question remain media_types.
I hope i was clear in explain my problem and thanks you in advance for your help!