Jackson adds backslash in json
Asked Answered
C

13

38

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?

Cantone answered 23/1, 2017 at 21:19 Comment(4)
When you put a String like your expected output into a String variable, the quotes must be escaped, or it would not be a valid string. What are you trying to do that the JSON string with the escaped quotes is not sufficient?Wheelman
I expect that it will be regulated by Jackson. But I tried return jsonInString.replaceAll("\\\\", ""). It does not work, I get the same result.Cecillececily
Your JAX-RS resource class looks more complicated than it should be. Have a look at my answer for more details.Augustine
better way to handle. please follow link:#64490778Flannelette
A
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.

Augustine answered 24/1, 2017 at 13:10 Comment(2)
I get [{name:\"Nick\..." in this case. But I change on return Response.ok(users).build().toString(); to avoid compilation problem. Because method build() return Response. One different thing: I use Jersey 1.13. This's dictated by dependencies on other modules. Will switching to 2nd version solve this problem?Cecillececily
@Kessler Jersey 1.x is pretty old. If you can, upgrade to Jersey 2.x. If you can't upgrade , check the Jersey 1.x documentation about JSON providers.Augustine
G
8

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

Ginnygino answered 23/1, 2017 at 22:41 Comment(3)
This's a great idea but it did not properly work for me. I get result json "[{name:\"Nick\"..., but I need [{"name":"Nick".... I tried to play around with these setting parameters but have not found the working combination.Cecillececily
These settings apply only to property names, not values.Disobedience
Thanks, I've found this question in Google trying to allow field names without quotes in jackson and your answer saved me.Phox
S
5

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?

Seedbed answered 23/1, 2017 at 21:46 Comment(1)
Yeah sure! But why does Jackson not understand it? I see simple string [{"name":"Nick",... in debug. But in Postman I get result as [{\"name\":\"Nick\",.... As far as I undestand value from debug contains backslashes to correct display string in debug. But what I need do with it? Real string doesn't content backslashes. Need I replace them in result string? I think it's pretty silly, and it does not work: return jsonInString.replaceAll("\\\\", "").Cecillececily
T
5

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;
    }
  }
Tragicomedy answered 17/9, 2019 at 9:53 Comment(1)
this should be the answerColorcast
B
3

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));

Bayonet answered 28/4, 2017 at 18:0 Comment(1)
this is what it was for me, thank you! been a while since i've used jackson, but when you convert json to a string (as in mapper.writeValueAsString), java escapes special chars, as others stated. but what is desired is true json in the output from the logger - so for that, as bahadar ali said, convert to a jsonnode, then log the jsonnnode object itself.Overhaul
B
3

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});

Bitterling answered 13/5, 2018 at 0:45 Comment(0)
A
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.

Augustine answered 24/1, 2017 at 13:10 Comment(2)
I get [{name:\"Nick\..." in this case. But I change on return Response.ok(users).build().toString(); to avoid compilation problem. Because method build() return Response. One different thing: I use Jersey 1.13. This's dictated by dependencies on other modules. Will switching to 2nd version solve this problem?Cecillececily
@Kessler Jersey 1.x is pretty old. If you can, upgrade to Jersey 2.x. If you can't upgrade , check the Jersey 1.x documentation about JSON providers.Augustine
G
2

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.

Godchild answered 28/11, 2021 at 5:18 Comment(1)
This is the correct answer. Easy to implement, and it just works.Slander
P
0

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.

Pollock answered 10/11, 2020 at 12:43 Comment(0)
C
0
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;
    }

}
Custommade answered 9/4, 2021 at 8:57 Comment(2)
It only works on the name of the field, resulting in "name":\"nick\"Altman
Where is the JsonWriteFeature from? import? –Tetroxide
M
0

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);
Machree answered 13/5, 2021 at 16:51 Comment(1)
Where is the JsonParser from? import?Altman
Y
0

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.

Ybarra answered 21/6, 2022 at 18:24 Comment(0)
F
0

Simplest solution would be:

String value = new ObjectMapper().writeValueAsString(jsonObject);
value = value.replace("\"", "'");

Detail: above code is replacing " with ' so when rendering JSON string via Jackson where wont be issue in parsing.

Faith answered 11/7, 2024 at 13:10 Comment(0)
K
-3

It should not be a problem, just you need to parse it in javascript and use it : JSON.parse(response)

Kauffman answered 20/1, 2018 at 1:34 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.