json formatting with moshi - java

Does anyone know a way to get moshi to produce a multi-line json with indentation ( for human consumption in the context of a config.json )
so from:
{"max_additional_random_time_between_checks":180,"min_time_between_checks":60}
to something like this:
{
"max_additional_random_time_between_checks":180,
"min_time_between_checks":60
}
I know other json-writer implementations can do so - but I would like to stick to moshi here for consistency

Now you can use .indent(" ") method on adapter for formatting.
final Moshi moshi = new Moshi.Builder().build();
String json = moshi.adapter(Dude.class).indent(" ").toJson(new Dude())

If you can deal with serializing the Object yourself, this should do the trick:
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import java.io.IOException;
import okio.Buffer;
public class MoshiPrettyPrintingTest {
private static class Dude {
public final String firstName = "Jeff";
public final String lastName = "Lebowski";
}
public static void main(String[] args) throws IOException {
final Moshi moshi = new Moshi.Builder().build();
final Buffer buffer = new Buffer();
final JsonWriter jsonWriter = JsonWriter.of(buffer);
// This is the important part:
// - by default this is `null`, resulting in no pretty printing
// - setting it to some value, will indent each level with this String
// NOTE: You should probably only use whitespace here...
jsonWriter.setIndent(" ");
moshi.adapter(Dude.class).toJson(jsonWriter, new Dude());
final String json = buffer.readUtf8();
System.out.println(json);
}
}
This prints:
{
"firstName": "Jeff",
"lastName": "Lebowski"
}
See prettyPrintObject() in this test file and the source code of BufferedSinkJsonWriter.
However, I haven't yet figured out whether and how it is possible to do this if you're using Moshi with Retrofit.

Related

Converting List to JSON in Java with correct extension

I am trying to create a List of errors with different codes and converting them to JSON using gson.
String jsonString = gson.toJson(Errors.getErrors());
And here is the class:
public class Errors {
private static List<SingleError> errors = new ArrayList<>();
public static List<SingleError> getErrors() {
return errors;
}
public static void addError(SingleError singleError) {
errors.add(singleError);
}
}
The output I get:
[
{
"code": "bad_signal"
}
]
The output I need:
{
"errors": [
{
"code": "bad_signal"
}
]
}
What am I missing in the Errors class the get the output I need?
It would be better if it would be added in the class without just adding string to json conversion.
EDIT
As schomsel suggested, I should use this line to get the output I need.
gson.toJson(Collections.singletonMap("errors", Errors.getErrors()))
And it did work but I failed to mention that I am also using Servlet to return the String and setting this header, which deletes the "errors".
resp.setHeader("Content-Type", "application/json");
What is the correct header I need to use here?
Obviously, you should understand that desired json representation is for Errors class itself and not contained erros list only so your code is to be tweaked so you can pass Errors class instance as input to - gson.toJson(...)
Two solutions ,
First Solution - make Errors fields and methods non - static and pass on Errors instance instead of errors List to call - gson.toJson(ErrorsInstance);
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
class Errors {
private List<SingleError> errors = new ArrayList<>();
public List<SingleError> getErrors() {
return errors;
}
public void addError(SingleError singleError) {
errors.add(singleError);
}
Second Solution - if fields & methods can't be made static then add a new method to get Errors instance via private constructor and then create Gson object from GsonBuilder so that static fields can be included during serialization.
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
class Errors {
// private constructor
private Errors(List<SingleError> errors) {
Errors.errors = errors;
}
private static List<SingleError> errors = new ArrayList<>();
public static List<SingleError> getErrors() {
return errors;
}
// New method to return Errors instance
public static Errors getErrorsInstance() {
return new Errors(errors);
}
public static void addError(SingleError singleError) {
errors.add(singleError);
}
}
//To include static fields during serialization & ignore only transient fields- if not done then json would be empty
GsonBuilder gsonBuilder = new GsonBuilder();
// Allowing the serialization of static fields
gsonBuilder.excludeFieldsWithModifiers(java.lang.reflect.Modifier.TRANSIENT);
// Creates a Gson instance based on the current configuration
Gson gson = gsonBuilder.create();
Errors errorsInstance = Errors.getErrorsInstance();
String jsonStringTest = gson.toJson(errorsInstance );
EDIT:
For Second solution, you wouldn't need a private constructor & new method - getErrorsInstance() . You can simply feed new Errors() to gson.toJson(...) . What you need is only static field inclusion in deserialization & same would hold true for solution # 1 too. So you don't need to modify any code, just make sure with GsonBuilder that static fields are included & pass on Errors instance instead of contained list.

How to prevent ObjectMapper from converting escaped unicode?

I'm using Jackson 2.4 in Java to do some JSON legwork. I make a call to a remote server with Apache HttpGet, deserialize the results with Jackson into a POJO, manipulate those results, and then serialize them with Jackson to push back to a remote server with HttpPost.
The issue I'm finding is that Jackson is translating unicode literals into unicode characters, which I need it not to do thanks to encoding issues on each end. For example, I might have this in the JSON:
"field1": "\u00a2"
But Jackson is converting the "\u00a2" to "ยข" when it's deserialized, which causes problems with the remote server. It has to be maintained as escaped unicode. If I use something like Apache EntityUtils (specifying UTF-8) or even make the call from my web browser to get the data, the escaped unicode is preserved, so I know that it's coming in properly from the server. If I have Jackson consume the input stream from the entity on the response, it does the conversion automatically.
I've tried writing with a JsonGenerator that is explicitly set to UTF-8 to write to the HttpPost. It didn't work, remote server still rejected it. I've dug through the configuration options for ObjectMapper and JsonParser, but I don't see anything that would override this behavior. Escaping non-ASCII, sure, but that's not what I need to do here. Maybe I'm missing something obvious, but I can't get Jackson to deserialize this string without replacing the escaped unicode.
EDIT: Well, my bad, the only literals having problems have 3 or 5 leading slashes, not just one. That's some screwiness, but Java seems to be what's unpacking it by default during the deserialization, even if the raw text that came back from the server preserves it. Still not sure how to get Java to preserve this without checking an insane amount of text.
What you are expecting is outside scope of Jackosn. It's java that converts the String while reading it. For same reason, if you have a properties file with value \u00a2 and read it using jdk API, you will get converted value. Depending on the file size, either you can double escape char \ before passing the string to Json or "escape" the string back using your Deserializer (only for string) and something like below:
Thank you
package com.test.json;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.Map;
public class Jackson {
static ObjectMapper _MAPPER = new ObjectMapper();
public static void main(String[] args) throws Exception {
String json = "{\"field1\": \"\\u00a2\",\"field2\": \"\\u00a2 this\",\"numberField\": 121212}";
SimpleModule testModule
= new SimpleModule("StOvFl", _MAPPER.version()).addDeserializer(String.class,
new UnEscapedSerializaer());
_MAPPER.registerModule(testModule);
Map m = _MAPPER.readValue(json, new TypeReference<Map<String, Object>>() {
});
System.out.println("m" + m);
}
}
class UnEscapedSerializaer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String s = jp.getValueAsString();
return org.apache.commons.lang.StringEscapeUtils.StringEscapeUtils.escapeJava(s);
}
}
Another way to custom Jackson's behavior is customized JsonParser. See jackson's source code of JsonFactory, ReaderBasedJsonParser;
The key methond is _finishString2() which is used to do 'decodeEscaped', so we can write a JsonParser extends ReaderBasedJsonParser and override the _finishString2 method:
public class MyJsonParser extends ReaderBasedJsonParser {
#Override
protected void _finishString2() throws IOException {
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
final int[] codes = _icLatin1;
final int maxCode = codes.length;
while (true) {
if (_inputPtr >= _inputEnd) {
if (!loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value");
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i < maxCode && codes[i] != 0) {
if (i == INT_QUOTE) {
break;
} else {
//c = _decodeEscaped();
//do nth
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = c;
}
_textBuffer.setCurrentLength(outPtr);
}
public static void main(String[] args) throws IOException {
String json = "{\"field1\": \"\\u00a2\",\"field2\": \"\\u00a2 this\",\"numberField\": 121212}";
ObjectMapper objectMapper = new ObjectMapper(new MyJsonParserFactory());
Object o = objectMapper.readValue(json, Object.class);
System.out.println(o);
}
}
Full demo code here

How to use GSON with no names?

I am using GSON to decrypt some JSON that I get from a PHP site, with the layout of:
[{"bellname":"Hey!","date":"2013-09-11"},{"bellname":"Haaaaey!","date":"2013-09-01"}]
I want to put this into a HashMap, and have seen that I need to create a class to do this. That is the part that I do not understand. This is what I have so far:
public class MySQLDB {
private BellName bellName;
private BellDate bellDate;
public static class BellName {
private String bn;
}
public static class BellDate {
private String date;
}
}
Where would I go from here? If you need to see my PHP code, here it is:
<?php
mysql_connect("localhost", "*******", "******");
mysql_select_db("tests");
$q1 = mysql_query("SELECT `bellname`, `date` FROM `bells`");
if($q1) {
$que1 = array();
while($a1 = mysql_fetch_assoc($q1)) {
$que1[] = $a1;
}
$json1 = json_encode($que1);
echo $json1;
}
?>
All this PHP code does is put a "mysql_fetch_assoc" reply into JSON.
Please help, if you could tell me how to fix this?
Thanks.
public class MySQLEntry {
public String bellname;
public String date;
}
// ...
Gson gson = new Gson();
MySQLEntry[] entryArray = gson.fromJson(yourJsonString, MySQLEntry[].class);
Answer by jpossi is the most appropriate. But just for you to know json object could be represented as Map in Java. And json array could be represented as List. So in your case you also can do this:
Gson gson = new Gson();
Type type = new TypeToken<List<Map<String,String>>>(){}.getType();
List<Map<String, String>> value = gson.fromJson("[{\"bellname\":\"Hey!\",\"date\":\"2013-09-11\"},{\"bellname\":\"Haaaaey!\",\"date\":\"2013-09-01\"}]", type);
System.out.println(value);
But deserializing in object is better then deserializing in collection of Strings in most cases.

Parsing JSON String as simple as possible with GSON

Using GSON, how can i return a single key from a Multidimensional Json String?
Here is the Multidimensional Json String:
{"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
I want to return the "session_token" key value.
I'm trying this:
class app {
static class Response {
String session_token;
}
public void getSessionToken() {
String x = {"statusCode":0,"statusDescription":"OK","data":{"user":{"id":xxx,"company_id":xxx,"account_type":"5","enable_locations":true,"intuit_user_id":null,"nick_name":"xxx","is_owner":"1","enabled":"1"},"session_token":"xxx"}}
Response r = new Gson().fromJson(x, Response.class);
System.out.println(r.session_token);
}
}
But with this, my r.session_token returns null.
You would need to use Gson's JsonParser class directly and extract the data from the parse tree:
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(myJsonString);
JsonObject jsonObject = element.getAsJsonObject();
String lastName = jsonObject.get("lastname").getAsString();
System.out.println(lastName);
That said, it's debatable whether this would save you any real time over:
(edited from comments below):
class App {
static class Response {
String lastname;
}
public static void main(String[] args) {
String myJsonString = "{\"name\":\"john\",\"lastname\":\"smith\"}";
Response r = new Gson().fromJson(myJsonString, Response.class);
System.out.println(r.lastname);
}
}
Gson will silently ignore the fact that there's more data in the JSON than you're interested in, and later on you might be interested in it, in which case it's trivial to add fields to your Response class.
Edit due to question changing:
You have a JSON object. It contains a field data whose value is an object. Inside that object you have a field session_token that you're interested in.
Either you have to navigate to that field through the parse tree, or you have to create Java classes that all will map to. The Java classes would resemble (at the bare minimum):
class Response {
Data data;
}
class Data {
String session_token;
}

Can I use Gson to serialize method-local classes and anonymous classes?

Example:
import com.google.gson.Gson;
class GsonDemo {
private static class Static {String key = "static";}
private class NotStatic {String key = "not static";}
void testGson() {
Gson gson = new Gson();
System.out.println(gson.toJson(new Static()));
// expected = actual: {"key":"static"}
System.out.println(gson.toJson(new NotStatic()));
// expected = actual: {"key":"not static"}
class MethodLocal {String key = "method local";}
System.out.println(gson.toJson(new MethodLocal()));
// expected: {"key":"method local"}
// actual: null (be aware: the String "null")
Object extendsObject = new Object() {String key = "extends Object";};
System.out.println(gson.toJson(extendsObject));
// expected: {"key":"extends Object"}
// actual: null (be aware: the String "null")
}
public static void main(String... arguments) {
new GsonDemo().testGson();
}
}
I would like these serializations especially in unit tests. Is there a way to do so?
I found Serializing anonymous classes with Gson, but the argumentation is only valid for de-serialization.
FWIW, Jackson will serialize anonymous and local classes just fine.
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
class MethodLocal {public String key = "method local";}
System.out.println(mapper.writeValueAsString(new MethodLocal()));
// {"key":"method local"}
Object extendsObject = new Object() {public String key = "extends Object";};
System.out.println(mapper.writeValueAsString(extendsObject));
// {"key":"extends Object"}
}
Note that Jackson by default won't access non-public fields through reflection, as Gson does, but it could be configured to do so. The Jackson way is to use regular Java properties (through get/set methods), instead. (Configuring it to use private fields does slow down the runtime performance, a bit, but it's still way faster than Gson.)

Categories