0

i have a json that is result of parser SearchAnalyticsQueryRequest to String

val request = new SearchAnalyticsQueryRequest()
  request.setStartDate("2017-05-09")
  request.setEndDate("2017-05-16")
  request.setDimensions(List("page", "query").asJava)
  request.setRowLimit(5000)
  request.setStartRow(0)

val gson = new GsonBuilder().create()
val result = gson.toJson(request)

result is:

{"dimensions":["page","query"],"endDate":"2017-05-16","rowLimit":5000,"startDate":"2017-05-09","startRow":0}

when i tried to do it backward

val backResult = gson.fromJson(result, classOf[SearchAnalyticsQueryRequest])

i`m got an exception:

java.lang.IllegalArgumentException: Can not set java.lang.Integer field com.google.api.services.webmasters.model.SearchAnalyticsQueryRequest.rowLimit to java.lang.Double
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:764)
at com.google.api.client.util.FieldInfo.setFieldValue(FieldInfo.java:245)
at com.google.api.client.util.FieldInfo.setValue(FieldInfo.java:206)
at com.google.api.client.util.GenericData.put(GenericData.java:103)
at com.google.api.client.util.GenericData.put(GenericData.java:47)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:188)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
at com.google.gson.Gson.fromJson(Gson.java:887)
at com.google.gson.Gson.fromJson(Gson.java:852)
at com.google.gson.Gson.fromJson(Gson.java:801)
at com.google.gson.Gson.fromJson(Gson.java:773)

if i remove rowLimit and startRow from json string - the SearchAnalyticsQueryRequest is parsed correctly

what i need to do to parse all json string to SearchAnalyticsQueryRequest

solved

val json = {"site":"<some url>","request":{"startDate":"2017-05-09","endDate":"2017-05-16","rowLimit":5000,"startRow":0,"dimensions":["query","page"],"dimensionFilterGroups":[{"filters":[{"dimension":"page","expression":"<some url>"}]}]}}

case class WebmastersRequest(site: String, request: SearchAnalyticsQueryRequest)

def fromGoogleJsonToSearchAnalytics(json: String): (Option[WebmastersRequest], Option[String]) = {
  val gson: Gson = new GsonBuilder().create()
  Try {
    val builder = new GsonBuilder()
    builder.registerTypeHierarchyAdapter(classOf[SearchAnalyticsQueryRequest], new JsonDeserializer[SearchAnalyticsQueryRequest]() {
      override def deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): SearchAnalyticsQueryRequest = {
        val jo = json.getAsJsonObject
        val req = new SearchAnalyticsQueryRequest()
        req.setAggregationType(Option(jo.get("aggregationType")).map(_.getAsString).orNull)
        req.setDimensionFilterGroups(Option(jo.get("dimensionFilterGroups")).map(o => gson.fromJson(o.getAsJsonArray, classOf[java.util.List[ApiDimensionFilterGroup]])).orNull)
        req.setDimensions(Option(jo.get("dimensions")).map(o => gson.fromJson(o.getAsJsonArray, classOf[java.util.List[String]])).orNull)
        req.setEndDate(Option(jo.get("endDate")).map(_.getAsString).orNull)
        req.setRowLimit(Option(jo.get("rowLimit")).map(i => Integer.valueOf(i.getAsInt)).orNull)
        req.setSearchType(Option(jo.get("searchType")).map(_.getAsString).orNull)
        req.setStartDate(Option(jo.get("startDate")).map(_.getAsString).orNull)
        req.setStartRow(Option(jo.get("startRow")).map(i => Integer.valueOf(i.getAsInt)).orNull)
        req
      }
    })

    builder.create().fromJson(json, classOf[WebmastersRequest])

  } match {
    case Success(x) => (Option(x), None)
    case Failure(e) =>
      e.printStackTrace()
      (None, Option(e.getMessage))
  }
}

This works! If someone tells me how to remove

val gson: Gson = new GsonBuilder().create()

I will be appreciate :)

HoTicE
  • 573
  • 2
  • 13

1 Answers1

4

JSON does not differentiate between integer and floating point fields, that's why when de-serializing a JSON string to GSON, the numeric fields will be considered as Double, and you'll get the error

Can not set java.lang.Integer field com.google.api.services.webmasters.model.SearchAnalyticsQueryRequest.rowLimit to java.lang.Double

You can use an adapter in order to parse the Double to integer as per this answer: https://stackoverflow.com/a/20524187/2611451

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {

    public JsonElement serialize(Double src, Type typeOfSrc,
                JsonSerializationContext context) {
            Integer value = (int)Math.round(src);
            return new JsonPrimitive(value);
        }
    });

Gson gs = gsonBuilder.create();
Community
  • 1
  • 1
KAD
  • 10,972
  • 4
  • 31
  • 73
  • It did not help – HoTicE May 16 '17 at 15:48
  • Please refer to a similar thread, http://stackoverflow.com/questions/15507997/how-to-prevent-gson-from-expressing-integers-as-floats. It has other alternative solutions to the same problem. – KAD May 16 '17 at 18:59