[java] gson.toJson ()에서 StackOverflowError가 발생합니다.

내 개체에서 JSON 문자열을 생성하고 싶습니다.

Gson gson = new Gson();
String json = gson.toJson(item);

이 작업을 시도 할 때마다 다음 오류가 발생합니다.

14:46:40,236 ERROR [[BomItemToJSON]] Servlet.service() for servlet BomItemToJSON threw exception
java.lang.StackOverflowError
    at com.google.gson.stream.JsonWriter.string(JsonWriter.java:473)
    at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:347)
    at com.google.gson.stream.JsonWriter.value(JsonWriter.java:440)
    at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:235)
    at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:220)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:200)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:843)

다음은 내 BomItem 클래스 의 속성입니다 .

private int itemId;
private Collection<BomModule> modules;
private boolean deprecated;
private String partNumber;
private String description; //LOB
private int quantity;
private String unitPriceDollar;
private String unitPriceEuro;
private String discount;
private String totalDollar;
private String totalEuro;
private String itemClass;
private String itemType;
private String vendor;
private Calendar listPriceDate;
private String unitWeight;
private String unitAveragePower;
private String unitMaxHeatDissipation;
private String unitRackSpace;

참조 된 BomModule 클래스의 속성 :

private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;

이 오류의 원인은 무엇입니까? 어떻게 고칠 수 있습니까?



답변

그 문제는 순환 참조가 있다는 것입니다.

에서 BomModule클래스는하기 참조하고 :

private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;

에 대한 자체 참조는 BomModule분명히 GSON이 전혀 좋아하지 않습니다.

해결 방법은 null재귀 루프를 방지하기 위해 모듈을 로 설정하는 것 입니다. 이렇게하면 StackOverFlow-Exception을 피할 수 있습니다.

item.setModules(null);

또는 키워드 를 사용하여 직렬화 된 json에 표시 하지 않으려 는 필드 를 표시합니다 transient. 예 :

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;


답변

Log4J 로거를 클래스 속성으로 사용할 때 다음과 같은 문제가 발생했습니다.

private Logger logger = Logger.getLogger(Foo.class);

이것은 로거를 만들 static거나 단순히 실제 기능으로 이동 하여 해결할 수 있습니다 .


답변

Realm을 사용 중이고이 오류가 발생하고 문제를 일으키는 객체가 RealmObject를 확장 realm.copyFromRealm(myObject)하는 경우 직렬화를 위해 GSON으로 전달하기 전에 모든 Realm 바인딩없이 복사본을 만드는 것을 잊지 마십시오 .

복사되는 객체 중 하나에 대해서만 이것을 놓쳤습니다. 스택 추적이 객체 클래스 / 유형의 이름을 지정하지 않기 때문에 깨달았습니다. 문제는 순환 참조로 인해 발생하지만 RealmObject 기본 클래스의 어딘가에 순환 참조이므로 자신의 하위 클래스가 아니므로 발견하기가 더 어렵습니다!


답변

SLaks가 말했듯이 객체에 순환 참조가 있으면 StackOverflowError가 발생합니다.

이를 수정하려면 개체에 TypeAdapter를 사용할 수 있습니다.

예를 들어, 객체에서 문자열 만 생성해야하는 경우 다음과 같이 어댑터를 사용할 수 있습니다.

class MyTypeAdapter<T> extends TypeAdapter<T> {
    public T read(JsonReader reader) throws IOException {
        return null;
    }

    public void write(JsonWriter writer, T obj) throws IOException {
        if (obj == null) {
            writer.nullValue();
            return;
        }
        writer.value(obj.toString());
    }
}

다음과 같이 등록하십시오.

Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();

또는 이와 같이 인터페이스가 있고 모든 하위 클래스에 어댑터를 사용하려는 경우 :

Gson gson = new GsonBuilder()
               .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>())
               .create();


답변

내 대답은 조금 늦었지만이 질문에는 아직 좋은 해결책이 없다고 생각합니다. 원래 여기 에서 찾았습니다 .

GSON로 당신은 당신이 필드를 표시 할 수 있습니다 않습니다 와 JSON에 포함하려면 @Expose이 같은를 :

@Expose
String myString;  // will be serialized as myString

다음을 사용하여 gson 객체를 만듭니다.

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

노출 하지 않는 순환 참조 . 그것은 나를 위해 속임수를 썼습니다!


답변

이 오류는 수퍼 클래스에 로거가있을 때 흔히 발생합니다. @Zar가 이전에 제안했듯이 로거 필드에 정적 을 사용할 수 있지만 이것도 작동합니다.

protected final transient Logger logger = Logger.getLogger(this.getClass());

추신은 아마 작동하고 @Expose 주석으로 여기에 대해 더 자세히 확인하십시오. https://stackoverflow.com/a/7811253/1766166에서 이에


답변

나도 같은 문제를 안고있어. 제 경우에는 직렬화 된 클래스의 생성자가 다음과 같이 컨텍스트 변수를 사용하기 때문입니다.

public MetaInfo(Context context)

이 인수를 삭제하면 오류가 사라졌습니다.

public MetaInfo()