programing

번호 처리 방법JSON 응답의 역직렬화 시 Gson을 사용하는 FormatException

bestprogram 2023. 3. 13. 21:10

번호 처리 방법JSON 응답의 역직렬화 시 Gson을 사용하는 FormatException

Gson과 함께 JSON의 응답을 읽고 있는데, 이 답변은 가끔 A/S가 반환됩니다.NumberFormatException예상대로int값이 빈 문자열로 설정됩니다.이런 종류의 예외에 대처하는 가장 좋은 방법이 무엇인지 궁금합니다.값이 빈 문자열일 경우 역직렬화는 0이어야 합니다.

예상 JSON 응답:

{
   "name" : "Test1",
   "runtime" : 90
}

그러나 실행 시 문자열이 비어 있을 수 있습니다.

{
   "name" : "Test2",
   "runtime" : ""
}

Java 클래스는 다음과 같습니다.

public class Foo
{
    private String name;
    private int runtime;
}

디시리얼라이제이션은 다음과 같습니다.

String input = "{\n" +
               "   \"name\" : \"Test\",\n" +
               "   \"runtime\" : \"\"\n" +
               "}";

Gson gson = new Gson();
Foo foo = gson.fromJson(input, Foo.class);

그 결과,com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty Stringint 값 대신 빈 문자열이 반환되기 때문입니다.

Gson에게 "유형의 필드를 역직렬화하고 번호가 있는 경우"라고 말할 수 있는 방법이 있습니까?Format Exception, 기본값 0"을 반환합니다.

이 문제를 해결하려면String의 유형으로서runtime대신 필드int그러나 이러한 오류를 처리하는 더 나은 방법이 있을 수 있습니다.

여기 제가 만든 예가 있습니다.Longtype. 이것이 더 나은 옵션입니다.

public class LongTypeAdapter extends TypeAdapter<Long> {

    @Override
    public Long read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        String stringValue = reader.nextString();
        try {
            Long value = Long.valueOf(stringValue);
            return value;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    @Override
    public void write(JsonWriter writer, Long value) throws IOException {
        if (value == null) {
            writer.nullValue();
            return;
        }
        writer.value(value);
    }
}

다음을 사용하여 어댑터 등록Gsonutil:

Gson gson = new GsonBuilder().registerTypeAdapter(Long.class, new LongTypeAdapter()).create();

상세한 것에 대하여는, 이 링크를 참조해 주세요.

처음에, 나는 Integer values에 대한 일반적인 커스텀 타입 어댑터를 쓰려고 했다.NumberFormatException0을 반환하지만 Gson은 TypeAdapters를 원시 유형으로 허용하지 않습니다.

java.lang.IllegalArgumentException: Cannot register type adapters for class java.lang.Integer

그 후 새로운 타입을 도입했습니다.FooRuntime를 위해runtime필드, 즉Foo클래스는 다음과 같습니다.

public class Foo
{
    private String name;
    private FooRuntime runtime;

    public int getRuntime()
    {
        return runtime.getValue();
    }
}

public class FooRuntime
{
    private int value;

    public FooRuntime(int runtime)
    {
        this.value = runtime;
    }

    public int getValue()
    {
        return value;
    }
}

유형 어댑터는 사용자 지정 직렬화 프로세스를 처리합니다.

public class FooRuntimeTypeAdapter implements JsonDeserializer<FooRuntime>, JsonSerializer<FooRuntime>
{
    public FooRuntime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
    {
        int runtime;
        try
        {
            runtime = json.getAsInt();
        }
        catch (NumberFormatException e)
        {
            runtime = 0;
        }
        return new FooRuntime(runtime);
    }

    public JsonElement serialize(FooRuntime src, Type typeOfSrc, JsonSerializationContext context)
    {
        return new JsonPrimitive(src.getValue());
    }
}

이 시점에서,GsonBuilder타입 어댑터를 등록하기 위해서, 빈 문자열은, 송신 대신에 0 으로 해석됩니다.NumberFormatException.

String input = "{\n" +
               "   \"name\" : \"Test\",\n" +
               "   \"runtime\" : \"\"\n" +
               "}";

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(FooRuntime.class, new FooRuntimeTypeAdapter());
Gson gson = builder.create();
Foo foo = gson.fromJson(input, Foo.class);

빠르고 쉬운 회피책 - 런타임의 멤버 유형 필드를 String으로 변경하고 int로 runtime을 반환하는 getter를 통해 액세스합니다.

public class Foo
{
    private String name;
    private String runtime;

    public int getRuntime(){
        if(runtime == null || runtime.equals("")){
            return 0;
        }
        return Integer.valueOf(trackId);
    }
}

=> json 역직렬화 불필요

빈 문자열이 있는지 확인하고 0을 반환하는 이 타입 어댑터를 만들었습니다.

public class IntegerTypeAdapter extends TypeAdapter<Number> {
@Override
public void write(JsonWriter jsonWriter, Number number) throws IOException {
    if (number == null) {
        jsonWriter.nullValue();
        return;
    }
    jsonWriter.value(number);
}

@Override
public Number read(JsonReader jsonReader) throws IOException {
    if (jsonReader.peek() == JsonToken.NULL) {
        jsonReader.nextNull();
        return null;
    }

    try {
        String value = jsonReader.nextString();
        if ("".equals(value)) {
            return 0;
        }
        return Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new JsonSyntaxException(e);
    }
}

}

다른 코멘트에 기재되어 있듯이 GSON 2.3.1에서는 원시 타입의 타입 어댑터를 등록할 수 있습니다.여기에서는 int 타입과 Integer 타입을 처리하는 타입 어댑터를 사용할 수 있습니다.문자열, 부울 및 늘의 경우 디폴트값이 0(또는 늘)으로 되어 있습니다.그러면 "runtime" : "5"와 같은 숫자가 포함된 문자열이 계속 해석됩니다.

public static final TypeAdapter<Number> UNRELIABLE_INTEGER = new TypeAdapter<Number>() {
    @Override
    public Number read(JsonReader in) throws IOException {
        JsonToken jsonToken = in.peek();
        switch (jsonToken) {
            case NUMBER:
            case STRING:
                String s = in.nextString();
                try {
                    return Integer.parseInt(s);
                } catch (NumberFormatException ignored) {
                }
                try {
                    return (int)Double.parseDouble(s);
                } catch (NumberFormatException ignored) {
                }
                return null;
            case NULL:
                in.nextNull();
                return null;
            case BOOLEAN:
                in.nextBoolean();
                return null;
            default:
                throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
        }
    }
    @Override
    public void write(JsonWriter out, Number value) throws IOException {
        out.value(value);
    }
};
public static final TypeAdapterFactory UNRELIABLE_INTEGER_FACTORY = TypeAdapters.newFactory(int.class, Integer.class, UNRELIABLE_INTEGER);

다음 코드로 등록할 수 있습니다.

Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(UNRELIABLE_INTEGER_FACTORY)
            .create();

이것이 콜 해석의 시행을 대체하는 통상의 JsonReader.nextInt()에 주의해 주세요.토큰의 Int 및 parseDouble을 사용하면 정수를 해석하기 위한 내부 로직이 복제됩니다.

이 될 수 있습니다.0 ★★★runtimeFormat Exception이이 될 수 에는 Format Exception을 합니다.[ Format Exception ( Format Exception ) ] format 、 [ Format Exception ]를 선택합니다.

이 솔루션은 Double type에서 작동합니다.이것은 비 프라이머리 타입에 대해서만 동작합니다.

public class DoubleGsonTypeAdapter implements JsonDeserializer<Double> {

    @Override
    public Double deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        Double result = null;
        try {
            result = jsonElement.getAsDouble();
        } catch (NumberFormatException e) {
            return result;
        }
        return result;
    }
}

모델:

@SerializedName("rateOfInterest")
public Double rateOfInterest;
@SerializedName("repaymentTenure")
public Double repaymentTenure;
@SerializedName("emiAmount")
public Double emiAmount;

개조 클라이언트:

Gson gson = new GsonBuilder().registerTypeAdapter(Double.class, new DoubleGsonTypeAdapter()) .create();

Retrofit retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

어댑터 없음

수 없이 forgive forgive forgive 、 forgive 、 forgive 、 forgive 、 forgive 、 forgive 、 forgive 、 forgive 、 forgive forgive , forgive forgive forgive forgive forgive 、 forgive forgive forgive forgive forgive forgive forgive forgive forgive forgive forgive forgive forgive forgive forgive Model Class to " " " 。String

예를 들어 나는 가지고 있었다.

data class Info(
    @SerializedName("name") val name : String?,
    @SerializedName("cover") val cover : String?,
    @SerializedName("releaseDate") val releaseDate : Int?,
    @SerializedName("last_modified") val last_modified : Int?,
    @SerializedName("rating") val rating : Int?)

나는 숫자와 마주하고 있었다.Format Exception으로 변경했습니다.

data class Info(
    @SerializedName("name") val name : String?,
    @SerializedName("cover") val cover : String?,
    @SerializedName("releaseDate") val releaseDate : String?,
    @SerializedName("last_modified") val last_modified : String?,
    @SerializedName("rating") val rating : String?)

이제 확인해보겠습니다.

if(!TextUtils.isEmpty(releaseDate){
//go ahead to use it
}

언급URL : https://stackoverflow.com/questions/8863429/how-to-handle-a-numberformatexception-with-gson-in-deserialization-a-json-respon