通过 Google 云端点传输 BigDecimal 时出现异常

Exception on transfering BigDecimal over Google Cloud Endpoints

我正在尝试通过云端点传输文章数据,而旧版应用程序 (Android) 使用的数据结构要求所有价格都采用 BigDecimal。这是我的实体 class:

import org.hibernate.annotations.GenericGenerator;

import java.math.BigDecimal;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;


@Entity
@Table(name = "articles")
public class Article {
    private int id;
    ....
    private BigDecimal price;


    @Id
    @Column(name = "_id")
    @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    .....

    @Basic
    @Column(name = "price", precision=7, scale=2)
    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

使用 API 资源管理器我得到以下结果:

{
 "items": [
  {
   "id": 2,
   ....
   "price": 5.00001,
   ...
  }
}

当尝试使用此代码在 android 中使用端点时:

String accountName = intent.getStringExtra(EXTRA_CREDENTIALS);
GoogleAccountCredential credentials = GoogleAccountCredential.usingAudience(this,
        AppConstants.AUDIENCE);
credentials.setSelectedAccountName(accountName);
if(articleApiService == null) {  // Only do this once
    articleApiService = AppConstants.getApiServiceHandle(credentials);
}

CollectionResponseArticle articles = null;
try {
    articles = articleApiService.listArticles().execute();
} catch (Exception e) {
    Log.e(TAG, e.getMessage());
}

我在 JSONParser.java 中得到以下异常:

java.lang.IllegalArgumentException: key price
        at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880)
        ...
 Caused by: java.lang.IllegalArgumentException: key price, field private java.util.List com.example.articlesendpoint.model.CollectionResponseArticle.items
        at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880)
        ...
 Caused by: java.lang.IllegalArgumentException: key price, field private java.util.List com.example.articlesendpoint.model.CollectionResponseArticle.items
        at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880)
        ...
 Caused by: java.lang.IllegalArgumentException: key price, field private com.example.articlesendpoint.model.BigDecimal com.example.articlesendpoint.model.Article.price
        at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:880)
        ...
 Caused by: java.lang.IllegalArgumentException: expected numeric type but got class com.example.articlesendpoint.model.BigDecimal
        at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:843)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:471)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:780)
            at com.google.api.client.json.JsonParser.parseArray(JsonParser.java:647)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:739)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:471)
            at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:780)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:381)
            at com.google.api.client.json.JsonParser.parse(JsonParser.java:354)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:87)
            at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81)
            at com.google.api.client.http.HttpResponse.parseAs(HttpResponse.java:459)
            at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
            at com.pos.android.dbsync.DBsyncService.onHandleIntent(DBsyncService.java:57)
            at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.os.HandlerThread.run(HandlerThread.java:61)

检查生成的端点客户端库时,有一个 BigDecimal 为我自动生成的模型 class 似乎无法被 android JSON 反序列化器识别:

/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://code.google.com/p/google-apis-client-generator/
 * (build: 2015-01-14 17:53:03 UTC)
 * on 2015-02-25 at 19:50:44 UTC 
 * Modify at your own risk.
 */

package com.example.articlesendpoint.model;

/**
 * Model definition for BigDecimal.
 *
 * <p> This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the articlesendpoint. For a detailed explanation see:
 * <a href="http://code.google.com/p/google-http-java-client/wiki/JSON">http://code.google.com/p/google-http-java-client/wiki/JSON</a>
 * </p>
 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class BigDecimal extends com.google.api.client.json.GenericJson {

  /**
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.Integer scale;

  /**
   * @return value or {@code null} for none
   */
  public java.lang.Integer getScale() {
    return scale;
  }

  /**
   * @param scale scale or {@code null} for none
   */
  public BigDecimal setScale(java.lang.Integer scale) {
    this.scale = scale;
    return this;
  }

  @Override
  public BigDecimal set(String fieldName, Object value) {
    return (BigDecimal) super.set(fieldName, value);
  }

  @Override
  public BigDecimal clone() {
    return (BigDecimal) super.clone();
  }

}

知道我做错了什么吗?

BigDecimal 不是受支持的 return 类型。 The supported return types are:

java.lang.String
java.lang.Boolean and boolean
java.lang.Integer and int
java.lang.Long and long
java.lang.Float and float
java.lang.Double and double
java.util.Date
com.google.api.server.spi.types.DateAndTime
com.google.api.server.spi.types.SimpleDate
Any enum
Any array or java.util.Collection of a parameter type

您有几个选择:

  1. 使用 @ApiTranformer - 在您的转换器中传递一个 Article 和 return 一个包装的 Article 实体,它表示价格值,如 String

  2. 忘掉转换器的东西,只装饰你的模型并确保你的端点方法return是装饰类型,比如

public WrappedArticle(Article article) {
    price = article.getPrice().toString();
    ....
}
  1. @JsonIgnore 注释 price 以便 Jackson 反序列化器(假设您使用的是默认值 json factory) 忽略 属性,并添加另一个 属性 类型的字符串。确保使用 @IgnoreSave 进行注释,以便 Objectify 不会将其存储为实体的一部分。
@IgnoreSave
private String priceStr;

现在您可以提供以下内容 getter:

public String getPriceStr() {
  return price.toString();
}

就是这样。任务完成!