programing

Gson 옵션 및 필수 필드

showcode 2023. 3. 6. 21:38
반응형

Gson 옵션 및 필수 필드

하면 좋을까요?Gson필수필드와 옵션필드의 비교

되어 있는지 여부에 네트워크 할 수 Gson무효

하고 방법gson.fromJson(json, mClassOfT);

예를 들어 다음과 같은 json이 있는 경우:

{"user_id":128591, "user_name":"TestUser"}

그리고 우리 반:

public class User {

    @SerializedName("user_id")
    private String mId;

    @SerializedName("user_name")
    private String mName;

    public String getId() {
        return mId;
    }

    public void setId(String id) {
        mId = id;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }
}

「」를 취득하기 입니까?Gson이 json을 하지 않으면 합니다.user_id ★★★★★★★★★★★★★★★★★」user_name 열쇠요?

구문 분석해야 할 값 중 하나와 옵션 값 중 하나가 필요한 경우가 많습니다.

이 사건을 글로벌하게 처리하기 위해 사용할 패턴이나 라이브러리가 있습니까?

감사해요.

필드 ' 필드'만 표시됩니다.null된 항목이 있는 합니다.

이를 위해 재사용 가능한 역직렬라이저와 주석을 소개합니다.가 현재에서의 커스텀 로 하는 더 POJO를 입니다.Gson오브젝트 자체로의 역직렬화 또는 주석 체크 아웃을 다른 메서드로 이동하여 역직렬화에서 사용합니다., 해, 「 처리」에으로, .JsonParseException할 수 하기 위해, 을 통해 검출할 수 있습니다.getCause()를 참조해 주세요.

이 모든 것이, 대부분의 경우, 이것은 유효합니다.

public class App
{

    public static void main(String[] args)
    {
        Gson gson =
            new GsonBuilder()
            .registerTypeAdapter(TestAnnotationBean.class, new AnnotatedDeserializer<TestAnnotationBean>())
            .create();

        String json = "{\"foo\":\"This is foo\",\"bar\":\"this is bar\"}";
        TestAnnotationBean tab = gson.fromJson(json, TestAnnotationBean.class);
        System.out.println(tab.foo);
        System.out.println(tab.bar);

        json = "{\"foo\":\"This is foo\"}";
        tab = gson.fromJson(json, TestAnnotationBean.class);
        System.out.println(tab.foo);
        System.out.println(tab.bar);

        json = "{\"bar\":\"This is bar\"}";
        tab = gson.fromJson(json, TestAnnotationBean.class);
        System.out.println(tab.foo);
        System.out.println(tab.bar);
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface JsonRequired
{
}

class TestAnnotationBean
{
    @JsonRequired public String foo;
    public String bar;
}

class AnnotatedDeserializer<T> implements JsonDeserializer<T>
{

    public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
    {
        T pojo = new Gson().fromJson(je, type);

        Field[] fields = pojo.getClass().getDeclaredFields();
        for (Field f : fields)
        {
            if (f.getAnnotation(JsonRequired.class) != null)
            {
                try
                {
                    f.setAccessible(true);
                    if (f.get(pojo) == null)
                    {
                        throw new JsonParseException("Missing field in JSON: " + f.getName());
                    }
                }
                catch (IllegalArgumentException ex)
                {
                    Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);
                }
                catch (IllegalAccessException ex)
                {
                    Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return pojo;

    }
}

출력:

이것은 foo입니다.여기는 술집입니다이것은 foo입니다.무효"main" 스레드 com.google.gson에 예외가 있습니다.JsonParse Exception:JSON에서 누락된 필드: foo

Brian Roach의 답변은 매우 좋지만, 때로는 다음과 같은 사항을 다루어야 합니다.

  • 모델의 슈퍼 클래스 특성
  • 어레이 내부의 속성

이러한 목적을 위해 다음 클래스를 사용할 수 있습니다.

/**
 * Adds the feature to use required fields in models.
 *
 * @param <T> Model to parse to.
 */
public class JsonDeserializerWithOptions<T> implements JsonDeserializer<T> {

    /**
     * To mark required fields of the model:
     * json parsing will be failed if these fields won't be provided.
     * */
    @Retention(RetentionPolicy.RUNTIME) // to make reading of this field possible at the runtime
    @Target(ElementType.FIELD)          // to make annotation accessible through reflection
    public @interface FieldRequired {}

    /**
     * Called when the model is being parsed.
     *
     * @param je   Source json string.
     * @param type Object's model.
     * @param jdc  Unused in this case.
     *
     * @return Parsed object.
     *
     * @throws JsonParseException When parsing is impossible.
     * */
    @Override
    public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
            throws JsonParseException {
        // Parsing object as usual.
        T pojo = new Gson().fromJson(je, type);

        // Getting all fields of the class and checking if all required ones were provided.
        checkRequiredFields(pojo.getClass().getDeclaredFields(), pojo);

        // Checking if all required fields of parent classes were provided.
        checkSuperClasses(pojo);

        // All checks are ok.
        return pojo;
    }

    /**
     * Checks whether all required fields were provided in the class.
     *
     * @param fields Fields to be checked.
     * @param pojo   Instance to check fields in.
     *
     * @throws JsonParseException When some required field was not met.
     * */
    private void checkRequiredFields(@NonNull Field[] fields, @NonNull Object pojo)
            throws JsonParseException {
        // Checking nested list items too.
        if (pojo instanceof List) {
            final List pojoList = (List) pojo;
            for (final Object pojoListPojo : pojoList) {
                checkRequiredFields(pojoListPojo.getClass().getDeclaredFields(), pojoListPojo);
                checkSuperClasses(pojoListPojo);
            }
        }

        for (Field f : fields) {
            // If some field has required annotation.
            if (f.getAnnotation(FieldRequired.class) != null) {
                try {
                    // Trying to read this field's value and check that it truly has value.
                    f.setAccessible(true);
                    Object fieldObject = f.get(pojo);
                    if (fieldObject == null) {
                        // Required value is null - throwing error.
                        throw new JsonParseException(String.format("%1$s -> %2$s",
                                pojo.getClass().getSimpleName(),
                                f.getName()));
                    } else {
                        checkRequiredFields(fieldObject.getClass().getDeclaredFields(), fieldObject);
                        checkSuperClasses(fieldObject);
                    }
                }

                // Exceptions while reflection.
                catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new JsonParseException(e);
                }
            }
        }
    }

    /**
     * Checks whether all super classes have all required fields.
     *
     * @param pojo Object to check required fields in its superclasses.
     *
     * @throws JsonParseException When some required field was not met.
     * */
    private void checkSuperClasses(@NonNull Object pojo) throws JsonParseException {
        Class<?> superclass = pojo.getClass();
        while ((superclass = superclass.getSuperclass()) != null) {
            checkRequiredFields(superclass.getDeclaredFields(), pojo);
        }
    }

}

우선 필수 필드를 마킹하기 위한 인터페이스(주석)에 대해 설명합니다.이 사용 예에 대해서는 나중에 설명하겠습니다.

    /**
     * To mark required fields of the model:
     * json parsing will be failed if these fields won't be provided.
     * */
    @Retention(RetentionPolicy.RUNTIME) // to make reading of this field possible at the runtime
    @Target(ElementType.FIELD)          // to make annotation accessible throw the reflection
    public @interface FieldRequired {}

★★★★★★★★★★★★★★★.deserialize메서드가 구현됩니다.평소와 즉, 결과 이 누락되어 있습니다. 결과에서 속성이 누락됨pojo 가지다null§:

T pojo = new Gson().fromJson(je, type);

다음 구문 체크합니다.pojo기동합니다.

checkRequiredFields(pojo.getClass().getDeclaredFields(), pojo);

다음, '있다'의 .pojo 의 슈퍼클래스:

checkSuperClasses(pojo);

경우에 어떤 경우에 따라서는요.SimpleModel its를 SimpleParentModel 성질을 .SimpleModel에 따라 가 붙어 있는 경우는, 「필요한 경우」라고 됩니다.SimpleParentModel의 것입니다.

그럼 한번 요?checkRequiredFields 이 '재산이 있다'의 인스턴스인지 아닌지를 확인합니다.List(array) - 이 하여 모든 (json array) - 필수 필드입니다.

if (pojo instanceof List) {
    final List pojoList = (List) pojo;
    for (final Object pojoListPojo : pojoList) {
        checkRequiredFields(pojoListPojo.getClass().getDeclaredFields(), pojoListPojo);
        checkSuperClasses(pojoListPojo);
    }
}

.pojo에 모음음음음음 , , 、 나 with with with with with with 。FieldRequired이이이.무효로컬 속속속 속속외예예예예예예예예예예예예다다다다다다.그렇지 않으면 현재 필드에 대해 다른 재귀적 검증 단계가 시작되고 필드의 상위 클래스 속성도 검사됩니다.

        for (Field f : fields) {
            // If some field has required annotation.
            if (f.getAnnotation(FieldRequired.class) != null) {
                try {
                    // Trying to read this field's value and check that it truly has value.
                    f.setAccessible(true);
                    Object fieldObject = f.get(pojo);
                    if (fieldObject == null) {
                        // Required value is null - throwing error.
                        throw new JsonParseException(String.format("%1$s -> %2$s",
                                pojo.getClass().getSimpleName(),
                                f.getName()));
                    } else {
                        checkRequiredFields(fieldObject.getClass().getDeclaredFields(), fieldObject);
                        checkSuperClasses(fieldObject);
                    }
                }

                // Exceptions while reflection.
                catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new JsonParseException(e);
                }
            }
        }

마지막 은 검토해야 .checkSuperClasses: 필수 pojo 의 슈퍼클래스:

    Class<?> superclass = pojo.getClass();
    while ((superclass = superclass.getSuperclass()) != null) {
        checkRequiredFields(superclass.getDeclaredFields(), pojo);
    }

.JsonDeserializerWithOptions다음과 합니다.하다

private class SimpleModel extends SimpleParentModel {

    @JsonDeserializerWithOptions.FieldRequired Long id;
    @JsonDeserializerWithOptions.FieldRequired NestedModel nested;
    @JsonDeserializerWithOptions.FieldRequired ArrayList<ListModel> list;

}

private class SimpleParentModel {

    @JsonDeserializerWithOptions.FieldRequired Integer rev;

}

private class NestedModel extends NestedParentModel {

    @JsonDeserializerWithOptions.FieldRequired Long id;

}

private class NestedParentModel {

    @JsonDeserializerWithOptions.FieldRequired Integer rev;

}

private class ListModel {

    @JsonDeserializerWithOptions.FieldRequired Long id;

}

알 수 .SimpleModel 예외 분석됩니다.

final Gson gson = new GsonBuilder()
    .registerTypeAdapter(SimpleModel.class, new JsonDeserializerWithOptions<SimpleModel>())
    .create();

gson.fromJson("{\"list\":[ { \"id\":1 } ], \"id\":1, \"rev\":22, \"nested\": { \"id\":2, \"rev\":2 }}", SimpleModel.class);

많은 기능을 할 수 . 예를 "은 "네스트된 오브젝트"로 . 예를 들어, 네스트된 객체에 대한 검증은 다음과 같습니다.FieldRequired수 없지만 할 수 .현재는 답변 범위를 벗어났지만 나중에 추가할 수 있습니다.

이것은 최소한의 코딩으로 범용 솔루션을 작성하는 간단한 솔루션입니다.

  1. Create @옵션 주석
  2. 첫 번째 옵션 표시.나머지는 선택 사항으로 간주됩니다.더 이른 시간이 필요한 것으로 가정합니다.
  3. 소스 Json 개체에 값이 있는지 확인하는 일반 '로더' 메서드를 만듭니다.@Optional 필드가 발생하면 루프가 정지합니다.

저는 서브클래스를 사용하고 있기 때문에 그른트 작업은 슈퍼클래스로 진행됩니다.

여기 슈퍼클래스 코드가 있습니다.

import com.google.gson.Gson;
import java.lang.reflect.Field;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
... 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Optional {
  public boolean enabled() default true;
}

그리고 grunt 작업 방법

@SuppressWarnings ("unchecked")
  public <T> T payload(JsonObject oJR,Class<T> T) throws Exception {
  StringBuilder oSB = new StringBuilder();
  String sSep = "";
  Object o = gson.fromJson(oJR,T);
  // Ensure all fields are populated until we reach @Optional
  Field[] oFlds =  T.getDeclaredFields();
  for(Field oFld:oFlds) {
    Annotation oAnno = oFld.getAnnotation(Optional.class);
    if (oAnno != null) break;
    if (!oJR.has(oFld.getName())) {
      oSB.append(sSep+oFld.getName());
      sSep = ",";
    }
  }
  if (oSB.length() > 0) throw CVT.e("Required fields "+oSB+" mising");
  return (T)o;
}

사용 예

public static class Payload {
  String sUserType ;
  String sUserID   ;
  String sSecpw    ;
  @Optional
  String sUserDev  ;
  String sUserMark ;
}

입력 코드

Payload oPL = payload(oJR,Payload.class);

이 경우 sUserDev 및 sUserMark는 옵션이며 나머지는 필수입니다.솔루션은 클래스가 필드 정의를 선언된 순서대로 저장한다는 사실에 의존합니다.

많이 찾아봤지만 좋은 답이 없어요.제가 선택한 솔루션은 다음과 같습니다.

JSON에서 설정해야 하는 모든 필드는 개체입니다(예: 상자 안의 정수, 부울 등).그런 다음 리플렉션을 사용하여 필드가 null이 아님을 확인할 수 있습니다.

public class CJSONSerializable {
    public void checkDeserialization() throws IllegalAccessException, JsonParseException {
        for (Field f : getClass().getDeclaredFields()) {
            if (f.get(this) == null) {
                throw new JsonParseException("Field " + f.getName() + " was not initialized.");
            }
        }
    }
}

이 클래스에서 JSON 객체를 도출할 수 있습니다.

public class CJSONResp extends CJSONSerializable {
  @SerializedName("Status")
  public String status;

  @SerializedName("Content-Type")
  public String contentType;
}

그런 다음 GSON을 사용하여 해석한 후 checkDeserialization을 호출하면 일부 필드가 null일 경우 보고됩니다.

(Brian Roache의 대답에서 영감을 얻었다.)

null이 다른 될 수 Brian의 가 없는 것 "null" "null" "Brian" "Brian" "Brian" "Brian" "Brian" "Brian" "Brian" "Brian" "Brian" "Brian" "Brian" ( "Brian "Brian")0를 참조해 주세요.

게다가 탈직렬기는 모든 타입에 등록해야 할 것 같습니다.에서는 「」를 합니다.TypeAdapterFactory(일부러)

로 하는 이 더 를 들어, 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 등).JsonOptional필요한 경우 모든 필드에 주석을 달지 않고 필드)를 선택합니다.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonOptional {
}

이 접근방식은 대신 필수 분야에 쉽게 적용할 수 있습니다.

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class AnnotatedTypeAdapterFactory implements TypeAdapterFactory {
  @Override
  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
    Class<? super T> rawType = typeToken.getRawType();

    Set<Field> requiredFields = Stream.of(rawType.getDeclaredFields())
            .filter(f -> f.getAnnotation(JsonOptional.class) == null)
            .collect(Collectors.toSet());

    if (requiredFields.isEmpty()) {
      return null;
    }

    final TypeAdapter<T> baseAdapter = (TypeAdapter<T>) gson.getAdapter(rawType);

    return new TypeAdapter<T>() {

      @Override
      public void write(JsonWriter jsonWriter, T o) throws IOException {
        baseAdapter.write(jsonWriter, o);
      }

      @Override
      public T read(JsonReader in) throws IOException {
        JsonElement jsonElement = Streams.parse(in);

        if (jsonElement.isJsonObject()) {
          ArrayList<String> missingFields = new ArrayList<>();
          for (Field field : requiredFields) {
            if (!jsonElement.getAsJsonObject().has(field.getName())) {
              missingFields.add(field.getName());
            }
          }
          if (!missingFields.isEmpty()) {
            throw new JsonParseException(
                    String.format("Missing required fields %s for %s",
                            missingFields, rawType.getName()));
          }
        }

        TypeAdapter<T> delegate = gson.getDelegateAdapter(AnnotatedTypeAdapterFactory.this, typeToken);
        return delegate.fromJsonTree(jsonElement);

      }
    };

  }
}

언급URL : https://stackoverflow.com/questions/21626690/gson-optional-and-required-fields

반응형