web-dev-qa-db-ja.com

Gsonが長い数値(json文字列)を科学表記形式に変換しないようにするにはどうすればよいですか?

Json文字列をJavaオブジェクトに変換し、それをlongとして表示する必要があります。json文字列は、長い数値の固定配列です。

{numbers
[ 268627104, 485677888, 506884800 ] }

変換するコードは、0で終わる数値を除くすべてのケースで正常に機能します。これらは、科学表記の数値形式に変換されます。

   public static Object fromJson(HttpResponse response, Class<?> classOf)
    throws IOException {
    InputStream instream = response.getResponseInputStream();                   

    Object obj = null;
    try {
        Reader reader = new InputStreamReader(instream, HTTP.UTF_8);

        Gson gson = new Gson();

        obj = gson.fromJson(reader, classOf); 

        Logger.d(TAG, "json --> "+gson.toJson(obj));
    } catch (UnsupportedEncodingException e) {
        Logger.e(TAG, "unsupported encoding", e);
    } catch (Exception e) {
        Logger.e(TAG, "json parsing error", e);
    }

    return obj;
}

実際の結果:Javaオブジェクト:268627104、485677888、5.068848E + 8

最後の数値が科学表記形式に変換されていることに注意してください。誰かがそれを回避したり、それを防止したり、元に戻したりするために何ができるかを提案できますか? Gson v1.7.1を使用しています

29
lini sax

文字列へのシリアル化がオプションである場合は、GSONを構成して次のようにすることができます。

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLongSerializationPolicy( LongSerializationPolicy.STRING );
Gson gson = gsonBuilder.create();

これは次のようなものを生成します:

{numbers : [ "268627104", "485677888", "506884800" ] }
17
Adam

別の回避策は、代わりにJsonParserクラスを使用することです。これにより、ユーザー定義のクラスではなくGsonオブジェクト表現(JsonElement)が返されますが、科学表記への変換の問題は回避されます。

import Java.lang.reflect.Type;
import Java.util.Map;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

public class GsonTest
{
    public static void main(String[] args)
    {
        String json = "{numbers:[268627104,485677888,506884800]}";

        Gson gson = new Gson();
        Type type = new TypeToken<Map<String, Object>>(){}.getType();
        Map<String, Object> jsonMap = gson.fromJson(json, type);
        System.out.println("Gson output:");
        System.out.println(jsonMap);

        JsonParser jsonParser = new JsonParser();
        JsonElement jsonElement = jsonParser.parse(json);
        System.out.println("JsonParser output:");
        System.out.println(jsonElement);
    }
}

コード出力:

Gson output:  
{numbers=[2.68627104E8, 4.85677888E8, 5.068848E8]}  
JsonParser output:  
{"numbers":[268627104,485677888,506884800]}
8
melbyts

同様の問題がありました。これは整数をdoubleに変換するだけでなく、この 関連する質問 で説明されているように、実際には特定の長い数値の精度を失います。

具体的には、この変換をObjectTypeAdapterreadメソッドに追跡しました。

_case NUMBER:
  return in.nextDouble();
_

TypeAdapterの変更されたObjectをプラグインすることは可能かもしれませんが、それを機能させることができなかったため、代わりにreadメソッド(Object read(JsonReader in))を自分のコードに追加し、上記の行を次のように変更します。

_case NUMBER:
    final String s = in.nextString();
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        // ignore
    }
    try {
        return Long.parseLong(s);
    } catch (NumberFormatException e) {
        // ignore
    }
    return Double.parseDouble(s);
_

Gsonがデフォルトでこれを実行したいと思います。

次に、他の接続部分を次のようなヘルパーメソッドに配置します。

_public static Object parse(final Reader r) {
    try (final JsonReader jr = new JsonReader(r)) {
        jr.setLenient(true);
        boolean empty = true;
        Object o = null;
        try {
            jr.peek();
            empty = false;
            o = read(jr);
        } catch (EOFException e) {
            if (!empty) {
                throw new JsonSyntaxException(e);
            }
        }
        if (o != null && jr.peek() != JsonToken.END_DOCUMENT) {
            throw new JsonIOException("JSON document was not fully consumed.");
        }
        return o;
    } catch (IOException e) {
        throw new JsonIOException(e);
    }
}
_

そこで、今ではnew Gson().fromJson(r, Object.class)の代わりにparse(r)を呼び出します。

これは、jsonデータを任意の構造で解析できるようにしたいのでうまくいきますが、ターゲットにしている特定のクラスがある場合は、おそらくそのクラスのメンバー内のObjectの出現を排除する必要があるだけです。

5
aditsu

同じ問題がありました。調査の結果、私が見つけたものです。

動作:

  • Gson
    小数部分がない数値の場合、GsonDoubleに変換します。
  • ジャクソン
    小数部分のない数値の場合、JacksonIntegerまたはLongに変換します。数値の大きさによって異なります。

可能な解決策:

  • Gsonの戻り値をDoubleからLongに明示的に変換します。
  • 代わりにJacksonを使用してください。
    私はこれを好みます。

コード-Jacksonのテスト

ParseNumberTest.Java:

import Java.util.List;

import org.testng.Assert;
import org.testng.annotations.Test;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * test - jackson parse numbers,
 * 
 * @author eric
 * @date Jan 13, 2018 12:28:36 AM
 */
public class ParseNumberTest {
    @Test
    public void test() throws Exception {
    String jsonFn = "numbers.json";

    ObjectMapper mapper = new ObjectMapper();

    DummyData dd = mapper.readValue(this.getClass().getResourceAsStream(jsonFn), DummyData.class);
    for (Object data : dd.dataList) {
        System.out.printf("data type: %s, value: %s\n", data.getClass().getName(), data.toString());
        Assert.assertTrue(data.getClass() == Double.class || data.getClass() == Long.class || data.getClass() == Integer.class);

        System.out.printf("%s\n\n", "------------");
    }
    }

    static class DummyData {
    List<Object> dataList;

    public List<Object> getDataList() {
        return dataList;
    }

    public void setDataList(List<Object> dataList) {
        this.dataList = dataList;
    }
    }
}

numbers.json:

{
    "dataList": [
        150000000000,
        150778742934,
        150000,
        150000.0
    ]
}

実行方法:

  • テストケースはJacksonTestNGに基づいています。
  • numbers.jsonと同じパッケージでParseNumberTest.Java
  • Testngテストとして実行すると、解析結果のタイプと値が出力されます。

出力:

data type: Java.lang.Long, value: 150000000000
------------

data type: Java.lang.Long, value: 150778742934
------------

data type: Java.lang.Integer, value: 150000
------------

data type: Java.lang.Double, value: 150000.0
------------

PASSED: test
2
Eric Wang

スマートではありませんが、まだ機能している方法は、番号の最初と最後に "を追加することです。その後、処理が完了したら削除します。

0
zzcron toby

以下のコードソリューションを番号Longに使用できます。

Document doc = documentCursor.next();  

JsonWriterSettings relaxed = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();  

CustomeObject obj = gson.fromJson(doc.toJson(relaxed), CustomeObject.class);
0
Deepak Gusain