36

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?

François Esthète
  • 855
  • 2
  • 9
  • 15
  • 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? – Alex Jan 23 '17 at 21:41
  • I expect that it will be regulated by Jackson. But I tried `return jsonInString.replaceAll("\\\\", "")`. It does not work, I get the same result. – François Esthète Jan 24 '17 at 13:00
  • Your JAX-RS resource class looks more complicated than it should be. Have a look at my [answer](http://stackoverflow.com/a/41829097/1426227) for more details. – cassiomolin Jan 24 '17 at 13:19
  • better way to handle. please follow link:https://stackoverflow.com/questions/64490278/how-to-avoid-jackson-escaping-double-quotes-in-java/64490466#64490466 – Brooklyn99 Oct 23 '20 at 05:12

12 Answers12

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

JuanGG
  • 834
  • 7
  • 11
  • 4
    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. – François Esthète Jan 24 '17 at 12:54
  • 5
    [These settings apply only to property names, not values.](https://github.com/FasterXML/jackson-databind/issues/715#issuecomment-76782907) – Matt Jan 25 '18 at 18:34
  • Thanks, I've found this question in Google trying to allow field names without quotes in jackson and your answer saved me. – Yan.Yurkin Feb 05 '19 at 04:58
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?

Tea Curran
  • 2,923
  • 2
  • 18
  • 22
  • 5
    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("\\\\", "")`. – François Esthète Jan 24 '17 at 12:14
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;
    }
  }
Deepraj jha
  • 71
  • 1
  • 7
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});

Bob
  • 780
  • 9
  • 10
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.

Community
  • 1
  • 1
cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • 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? – François Esthète Jan 24 '17 at 13:39
  • @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](https://jersey.java.net/documentation/1.18/json.html). – cassiomolin Jan 24 '17 at 13:43
2

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

Bahadar Ali
  • 173
  • 1
  • 3
  • 14
  • 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. – liltitus27 Sep 07 '20 at 17:13
1

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.

Aadam
  • 1,521
  • 9
  • 30
  • 60
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.

Cheloute
  • 783
  • 2
  • 11
  • 27
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;
    }

}
useless9276
  • 29
  • 1
  • 8
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);
Tarun Sawlani
  • 34
  • 1
  • 4
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.

Manas Singh
  • 65
  • 1
  • 12
-3

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

Sujan
  • 15
  • 3