I'm using Google GSON library in my android app to parse json response from server.
In some cases API retrieve html codes in response (e.g »). Is it possible to tell Google GSON to parse that codes and convert to symbol that associated with that code?
Gson does not have anything built in that would do that, no.
The only way to have Gson do this for you would be to write a custom deserializer for String that would decode the strings. You'd also need another library to do the HTML decoding since Java itself doesn't include anything.
Here's an example using the Apache Commons Lang library to do the decoding:
public class App
{
String foo;
public static void main(String[] args) throws MalformedURLException, IOException
{
String json = "{\"Text\":\"«Some message»\"}";
Gson g =
new GsonBuilder()
.registerTypeAdapter(String.class, new MyStringDeserializer()).create();
Type t = new TypeToken<Map<String,String>>(){}.getType();
Map<String,String> map = g.fromJson(json, t);
System.out.println(map.get("Text"));
}
}
class MyStringDeserializer implements JsonDeserializer<String>
{
#Override
public String deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
String s = StringEscapeUtils.unescapeHtml3(je.getAsString());
return s;
}
}
Output:
«Some message»
Related
I'm trying to create a dynamic response based converter
using retrofit, As for now I have 2 different answers returning from the server - one represents a failure and one represent a valid response How can I try and parse two different objects using the same adapter\callabck?
You can parse it as a java bean if data are json data.
You can use Gson to parse it.
1 Add lib
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.7'
2 Create Retrofit
private Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Just add a Gson converter.
For example,
// success
{"retcode":0,"result":{"vfwebqq":"xxxx"}}
// failed
{"retcode":100,"result":{}}
3 Create a bean to receive data.
public class Result {
public String retcode;
public Info result;
public static class Info {
public String vfwebqq;
}
}
4 Then you can return a bean object in retrofit interface.
#GET("xxx")
Result getHome();
Actually I'm not quite in what are you talking about and what exact issue you are facing. But the first thing that pops out of my head is just to provide custom JsonDeserializer. It should look like smth like this :
public class CustomDeserializer implements JsonDeserializer<List<CustomData>> {
#Override
public List<CustomData> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
List<CustomData> customDataSet = new ArrayList<>();
Iterator<JsonElement> iterator = ((JsonObject) json).get("data").getAsJsonObject().get(
"records").getAsJsonArray().iterator();
while (iterator.hasNext()) {
JsonElement element = iterator.next();
CustomData customData = ServiceGenerator.mGson.fromJson(element, CustomData.class);
customDataSet.add(customData);
}
return customDataSet;
}
}
That's just a custom parser class example which is applied to RetrofitBuilder just to make life easier(maybe).
Afterwards you need to :
Type listType = new TypeToken<List<CustomData>>() {
}.getType();
mGson = new GsonBuilder().registerTypeAdapter(listType, new CustomDeserializer()).create();
builder =
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(mGson))
.baseUrl(API_BASE_URL);
retrofit = builder.build();
Based on your question, i believe this site helps with your challenge:
https://futurestud.io/tutorials/retrofit-2-introduction-to-multiple-converters
I have the following pojo:
public class PageRedirect implements Parcelable {
#SerializedName("method") private String method;
#SerializedName("url") private String url;
#SerializedName("parameters") private Parameters parameters;
//#SerializedName("parameters") private String params;
......}
The parameters field is changing depends on some parameter with the originating API. So sometimes it is {} "json object" and if it is empty, it is array []. I know this is a fault in the Backend. But I would like to find a quick work around... Instead of parsing the parameters, I just would like to get it as a string as the commented line and then I will process it. Any ideas?
When creating your instance of Gson, you can set a custom class deserializer as follows:
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Parameter.class, new ParameterTypeAdapter());
final Gson gson = gsonBuilder.create();
PageRedirect pageRedirect = gson.fromJson(yourJsonString, PageRedirect.class);
Then you can create your ParameterTypeAdapter as follows:
public class ParameterTypeAdapter extends TypeAdapter<Parameter> {
#Override
public void write(JsonWriter out, Calendar value) throws IOException {
}
#Override
public Calendar read(JsonReader in) throws IOException {
// do your parsing here
}
You can find more info on it here and here.
EDIT:
If you just want to defer parsing to another moment, you can store your "parameters" field as a JsonElement:
#SerializedName("parameters") private JsonElement parameters;
Afterwards, just convert it to String by using parameters.toString();
In async mode retrofit calls
public void success(T t, Response rawResponse)
were t is the converted response, and rawResponse is the raw response. This provides you with access to both the raw response and the converted response.
In sync mode you can get either the converted response OR the raw response
converted response
#GET("/users/list")
List<User> userList();
raw response
#GET("/users/list")
Response userList();
The Response object does have a method to get the body
TypedInput getBody()
and the retrofit api does have a converter class that can convert this to a java object
Object fromBody(TypedInput body,Type type)
But I can not figure out how to get an instance of the Converter object
I might be able to create an instance of the Converter class, but that would require knowledge of the Gson object used to create the RestAdapter, which I may not have access to. Ideally, I would like obtain a reference to the converter object directly the RestAdpater.
Any of the following will answer my question:
Is there a way to get a reference to the default Converter used by retrofit?
Does anyone know how the default Converter is constructed? (there is no default constructor and there are two Constructors public GsonConverter(Gson gson) and public GsonConverter(Gson gson, String charset)
Is there any other way to get both the raw and converted response in sync mode?
Here's an example of a StringConverter class that implements the Converter found in retrofit. Basically you'll have to override the fromBody() and tell it what you want.
public class StringConverter implements Converter {
/*
* In default cases Retrofit calls on GSON which expects a JSON which gives
* us the following error, com.google.gson.JsonSyntaxException:
* java.lang.IllegalStateException: Expected BEGIN_OBJECT but was
* BEGIN_ARRAY at line x column x
*/
#Override
public Object fromBody(TypedInput typedInput, Type type)
throws ConversionException {
String text = null;
try {
text = fromStream(typedInput.in());
} catch (IOException e) {
e.printStackTrace();
}
return text;
}
#Override
public TypedOutput toBody(Object o) {
return null;
}
// Custom method to convert stream from request to string
public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}
Applying this to your request you'll have to do the following:
// initializing Retrofit's rest adapter
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ApiConstants.MAIN_URL).setLogLevel(LogLevel.FULL)
.setConverter(new StringConverter()).build();
I'm working with a REST API that returns a JSON document that starts as follows and includes a "collection" of items with string IDs like "ABC". Note the "routes" field, which contains a series of fields called "ABC", "ABD", "ABE" etc, however routes is not represented as an array in the json, so all these
{
"status":true,
"page":1,
"per_page":500,
"total_count":1234,
"total_pages":8,
"total_on_page":500,
"routes":{
"ABC":[
{
"value":22313,
<......>
I'm using Retrofit and the problem is the routes field is not an array (despite the fact conceptually it certainly is) and Retrofit/Gson require me to create a model object for routes with field vars abc, abd, etc - this is not practical as the data changes. For various reasons changing the server API is hard, so I'm looking to work around this on the Android client.
I figure these are options:
Intercept the JSON document before it reaches Gson and tweak the document, possibly with a customised Gson parser, or by intercepting the HTTP response.
Bypass the JSON parsing, and acquire the JSON document from Retrofit (I've yet to figure out how to do this, or if it's possible)
Use some feature of Retrofit I'm unaware of to map field names to a collection.
I'd appreciate help, especially if there's a quick and easy way to resolve this.
It turns out that Retrofit's use of Gson by default makes it fairly easy to add a custom deserialiser to handle the portion of the JSON document that was the problem.
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(ApiDefinition.BASE_URL)
.setConverter(getGsonConverter())
.build();
public Converter getGsonConverter() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(RouteList.class, new RouteTypeAdapter())
.create();
return new GsonConverter(gson);
}
public class RouteTypeAdapter implements JsonDeserializer<RouteList> {
#Override
public RouteList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
RouteList routeList = new RouteList();
JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String,JsonElement> elementJson : jsonObject.entrySet()){
RouteList wardsRoutes = gson.fromJson(elementJson.getValue().getAsJsonArray(), RouteList.class);
routeList.addAll(wardsRoutes);
}
return routeList;
}
}
After calling RestService, don't use Model Name as argument, you have to use Default Response class from retrofit library.
RestService Method
#FormUrlEncoded
#POST(GlobalVariables.LOGIN_URL)
void Login(#Field("email") String key, #Field("password") String value, Callback<Response> callback);
Calling method in Activity
getService().Login(email, password, new MyCallback<Response>(context, true, null)
{
#Override
public void failure(RetrofitError arg0)
{
// TODO Auto-generated method stub
UtilitySingleton.dismissDialog((BaseActivity<?>) context);
System.out.println(arg0.getResponse());
}
#Override
public void success(Response arg0, Response arg1)
{
String result = null;
StringBuilder sb = null;
InputStream is = null;
try
{
is = arg1.getBody().in();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
result = sb.toString();
System.out.println("Result :: " + result);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
I'm trying to serialize a list of BasicNameValuePairs using type adapters and Gson
ArrayList<BasicNameValuePair> kvp=new ArrayList<BasicNameValuePair>();
kvp.add(new BasicNameValuePair("car","ferrari"));
kvp.add(new BasicNameValuePair("speed","fast"));
this is the result I want
{"car":"ferrari","speed":"fast"}
instead of this
[{"name":"car","value":"ferrari"},{"name":"speed","value":"fast"}]
To serialize this according to specification you need to make a custom type adapter that will handle the generic list. First create the class that will do the proper formatting on the output.
public class KeyValuePairSerializer extends TypeAdapter<List<BasicNameValuePair>> {
#Override
public void write(JsonWriter out, List<BasicNameValuePair> data) throws IOException {
out.beginObject();
for(int i=0; i<data.size();i++){
out.name(data.get(i).getName());
out.value(data.get(i).getValue());
}
out.endObject();
}
/*I only need Serialization*/
#Override
public List<BasicNameValuePair> read(JsonReader in) throws IOException {
return null;
}
}
Then use a custom Gson builder to use that type adapter to create the proper JSON string.
GsonBuilder gsonBuilder= new GsonBuilder();
gsonBuilder.registerTypeAdapter(KeyValuePairSerializer.class, new KeyValuePairSerializer());
Gson gson=gsonBuilder.create();
Logger.e(getClass().getSimpleName(),gson.toJson(kvp, KeyValuePairSerializer.class));