gson.toJson () ném StackOverflowError


87

Tôi muốn tạo một Chuỗi JSON từ đối tượng của mình:

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

Mỗi khi tôi cố gắng làm điều này, tôi gặp lỗi này:

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)

Đây là các thuộc tính của lớp BomItem của tôi :

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;

Các thuộc tính của lớp BomModule được tham chiếu của tôi :

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

Bất kỳ ý tưởng nào gây ra lỗi này? Làm thế nào tôi có thể sửa chữa nó?


Có thể xảy ra nếu bạn đặt một cá thể đối tượng bên trong chính nó ở đâu đó bên trong gson.
Christophe Roussy

Các ngoại lệ thua các nguyên nhân gốc rễ một bắt đầu đăng nhập với JsonWriter.java:473), làm thế nào để xác định nguyên nhân gốc rễ của stackoverflow Gson
Siddharth

Câu trả lời:


86

Vấn đề đó là bạn tham khảo vòng tròn.

Trong BomModulelớp học mà bạn đang tham chiếu đến:

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

Bản thân đó BomModulerõ ràng là không được GSON thích chút nào.

Một cách giải quyết là chỉ cần đặt các mô-đun nullđể tránh lặp lại đệ quy. Bằng cách này, tôi có thể tránh được StackOverFlow-Exception.

item.setModules(null);

Hoặc đánh dấu các trường bạn không muốn hiển thị trong json được tuần tự hóa bằng cách sử dụng transienttừ khóa, ví dụ:

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

Có, một đối tượng BomModule có thể là một phần của một đối tượng BomModule khác.
nimrod

Nhưng đó có phải là một vấn đề? 'Bộ sưu tập <BomModule> mô-đun' chỉ là một bộ sưu tập và tôi nghĩ gson có thể tạo ra một mảng đơn giản từ nó?
nimrod

@dooonot: Có đối tượng nào trong bộ sưu tập tham chiếu đến đối tượng mẹ của chúng không?
SLaks

Tôi không chắc liệu tôi có hiểu bạn đúng không, nhưng có. Vui lòng xem câu hỏi cập nhật ở trên.
nimrod

@dooonot: Như tôi đã nghi ngờ, nó đi vào một vòng lặp vô hạn khi tuần tự hóa các tập hợp cha và con. Bạn mong đợi nó viết JSON loại nào?
SLaks

29

Tôi đã gặp sự cố này khi tôi có trình ghi Log4J làm thuộc tính lớp, chẳng hạn như:

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

Điều này có thể được giải quyết bằng cách tạo bộ ghi nhật ký statichoặc đơn giản bằng cách chuyển nó vào (các) chức năng thực tế.


4
Hoàn toàn tuyệt vời bắt. Việc tự tham chiếu đến lớp rõ ràng là không được GSON thích chút nào. Đã cứu tôi rất nhiều đau đầu! +1
christopher

1
một cách khác để giải quyết nó, là bằng cách thêm modifier thoáng qua đến lĩnh vực
gawi

trình ghi nhật ký chủ yếu phải là tĩnh. Nếu không, bạn sẽ phải trả chi phí để có được thể hiện Logger đó cho mỗi lần tạo đối tượng, đây có thể không phải là điều bạn muốn. (Chi phí không phải là nhỏ)
stolsvik

26

Nếu bạn đang sử dụng Realm và bạn gặp lỗi này và đối tượng gây ra sự cố mở rộng RealmObject, đừng quên realm.copyFromRealm(myObject)tạo một bản sao mà không có tất cả các ràng buộc Realm trước khi chuyển đến GSON để tuần tự hóa.

Tôi đã bỏ lỡ làm điều này chỉ vì một trong số một loạt các đối tượng đang được sao chép ... khiến tôi mất nhiều thời gian để nhận ra vì dấu vết ngăn xếp không đặt tên cho lớp / loại đối tượng. Vấn đề là do tham chiếu vòng tròn gây ra, nhưng đó là tham chiếu vòng ở đâu đó trong lớp cơ sở RealmObject, không phải lớp con của riêng bạn, điều này khiến bạn khó phát hiện hơn!


1
Đúng rồi! Trong trường hợp của tôi, hãy thay đổi danh sách đối tượng của tôi được truy vấn trực tiếp từ vùng thành ArrayList <Hình ảnh> copyList = new ArrayList <> (); for (Hình ảnh hình ảnh: hình ảnh) {copyList.add (domains.copyFromRealm (hình ảnh)); }
Ricardo Mutti

Sử dụng lĩnh vực, đó là chính xác những giải pháp mà giải quyết vấn đề, nhờ
Jude Fernandes

13

Như SLaks đã nói StackOverflowError xảy ra nếu bạn có tham chiếu vòng tròn trong đối tượng của mình.

Để khắc phục, bạn có thể sử dụng TypeAdapter cho đối tượng của mình.

Ví dụ: nếu bạn chỉ cần tạo Chuỗi từ đối tượng của mình, bạn có thể sử dụng bộ điều hợp như sau:

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());
    }
}

và đăng ký nó như thế này:

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

hoặc như thế này, nếu bạn có giao diện và muốn sử dụng bộ điều hợp cho tất cả các lớp con của nó:

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

9

Câu trả lời của tôi là hơi muộn, nhưng tôi nghĩ câu hỏi này vẫn chưa có lời giải hay. Tôi đã tìm thấy nó ban đầu ở đây .

Với Gson bạn có thể đánh dấu các trường bạn làm muốn được bao gồm trong json với @Exposenhư thế này:

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

và tạo đối tượng gson với:

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

Thông tư tham khảo bạn chỉ không tiết lộ. Điều đó đã đánh lừa tôi!


Bạn có biết nếu có một chú thích làm điều ngược lại với điều này? Có 4 trường tôi cần bỏ qua và hơn 30 trường tôi cần đưa vào.
jDub9

@ jDub9 Xin lỗi vì câu trả lời muộn của tôi, nhưng tôi đang đi nghỉ. Hãy xem câu trả lời này . Hy vọng nó giải quyết vấn đề của bạn
ffonz

3

Lỗi này thường gặp khi bạn có trình ghi nhật ký trong siêu lớp của mình. Như @Zar đã đề xuất trước đây, bạn có thể sử dụng tĩnh cho trường ghi nhật ký của mình, nhưng điều này cũng hoạt động:

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

PS có thể nó sẽ hoạt động và với chú thích @Expose, hãy kiểm tra thêm về điều này tại đây: https://stackoverflow.com/a/7811253/1766166


1

Tôi có cùng một vấn đề. Trong trường hợp của tôi, lý do là hàm tạo của lớp được tuần tự hóa của tôi nhận biến ngữ cảnh, như thế này:

public MetaInfo(Context context)

Khi tôi xóa đối số này, lỗi đã biến mất.

public MetaInfo()

1
Tôi gặp sự cố này khi chuyển tham chiếu đối tượng dịch vụ làm ngữ cảnh. Khắc phục là làm cho biến ngữ cảnh tĩnh trong lớp sử dụng gson.toJson (this).
user802467

@ user802467 ý bạn là dịch vụ android?
Preetam

1

Chỉnh sửa: Xin lỗi vì lỗi của tôi, đây là câu trả lời đầu tiên của tôi. Cảm ơn lời khuyên của bạn.

Tôi tạo Trình chuyển đổi Json của riêng mình

Giải pháp chính mà tôi đã sử dụng là tạo một tập đối tượng cha mẹ cho mỗi tham chiếu đối tượng. Nếu một tham chiếu phụ trỏ đến đối tượng mẹ đã tồn tại, nó sẽ loại bỏ. Sau đó, tôi kết hợp với một giải pháp bổ sung, giới hạn thời gian tham chiếu để tránh vòng lặp vô tận trong mối quan hệ hai chiều giữa các thực thể.

Mô tả của tôi không quá tốt, mong nó giúp ích cho các bạn.

Đây là đóng góp đầu tiên của tôi cho cộng đồng Java (giải pháp cho vấn đề của bạn). Bạn có thể kiểm tra nó;) Có một tệp README.md https://github.com/trannamtrung1st/TSON


2
Liên kết đến một giải pháp được hoan nghênh, nhưng hãy đảm bảo rằng câu trả lời của bạn hữu ích nếu không có nó: thêm ngữ cảnh xung quanh liên kết để những người dùng đồng nghiệp của bạn sẽ biết nó là gì và tại sao nó ở đó, sau đó trích dẫn phần có liên quan nhất của trang bạn ' đang liên kết lại trong trường hợp trang đích không có sẵn. Các câu trả lời ít hơn một liên kết có thể bị xóa.
Paul Roub

2
Tự quảng cáo Chỉ liên kết đến thư viện hoặc hướng dẫn của riêng bạn không phải là một câu trả lời hay. Liên kết với nó, giải thích tại sao nó giải quyết được vấn đề, cung cấp mã về cách làm như vậy và tuyên bố từ chối rằng bạn đã viết nó sẽ tạo ra câu trả lời tốt hơn. Hãy xem: Điều gì biểu hiện sự tự quảng cáo “Tốt”?
Shree

Cám ơn rất nhiều. Tôi đã chỉnh sửa câu trả lời của mình. Hy vọng nó sẽ là tốt: D
Trần Nam Trung

Tương tự như những gì những người bình luận khác đã nói, bạn nên hiển thị những phần quan trọng nhất của mã trong bài đăng của mình. Ngoài ra, bạn không cần phải xin lỗi về những sai lầm trong câu trả lời của mình.
0xCursor

0

Trong Android, tràn ngăn xếp gson hóa ra là khai báo của Trình xử lý. Đã chuyển nó sang một lớp không được giải mã.

Dựa trên khuyến nghị của Zar, tôi đã đặt trình xử lý tĩnh khi điều này xảy ra trong một phần mã khác. Làm cho trình xử lý tĩnh cũng hoạt động.


0

BomItemtham chiếu đến BOMModule( Collection<BomModule> modules) và BOMModuletham chiếu đến BOMItem( Collection<BomItem> items). Thư viện Gson không thích tham chiếu vòng tròn. Xóa sự phụ thuộc vòng tròn này khỏi lớp của bạn. Tôi cũng đã phải đối mặt với vấn đề tương tự trong quá khứ với gson lib.


0

Tôi đã có vấn đề này xảy ra cho tôi khi tôi đặt:

Logger logger = Logger.getLogger( this.getClass().getName() );

trong đối tượng của tôi ... điều này hoàn toàn hợp lý sau một giờ hoặc lâu hơn gỡ lỗi!



0

Tránh các cách giải quyết không cần thiết, chẳng hạn như đặt giá trị thành null hoặc tạo trường tạm thời. Cách đúng để thực hiện việc này là chú thích một trong các trường với @Expose và sau đó yêu cầu Gson chỉ tuần tự hóa các trường có chú thích:

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

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

0

Tôi đã gặp sự cố tương tự trong đó lớp có biến InputStream mà tôi không thực sự phải tiếp tục. Do đó, việc thay đổi nó thành Tạm thời đã giải quyết được vấn đề.


0

Sau một thời gian đấu tranh với vấn đề này, tôi tin rằng tôi có một giải pháp. Vấn đề là ở các kết nối hai chiều chưa được giải quyết và cách biểu diễn các kết nối khi chúng đang được tuần tự hóa. Cách để sửa hành vi đó là "nói" gsoncách tuần tự hóa các đối tượng. Với mục đích đó chúng tôi sử dụng Adapters.

Bằng cách sử dụng, Adapterschúng ta có thể biết gsoncách tuần tự hóa mọi thuộc tính từ Entitylớp của bạn cũng như những thuộc tính nào cần tuần tự hóa.

Để FooBarlà hai thực thể FooOneToManyquan hệ với BarBarManyToOnequan hệ với Foo. Chúng tôi xác định Barbộ điều hợp để khi gsontuần tự hóa Bar, bằng cách xác định cách tuần tự hóa Footừ quan điểm Bartham chiếu tuần hoàn sẽ không thể thực hiện được.

public class BarAdapter implements JsonSerializer<Bar> {
    @Override
    public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("id", bar.getId());
        jsonObject.addProperty("name", bar.getName());
        jsonObject.addProperty("foo_id", bar.getFoo().getId());
        return jsonObject;
    }
}

Đây foo_idđược sử dụng để đại diện cho Foothực thể sẽ được tuần tự hóa và sẽ gây ra sự cố tham chiếu theo chu kỳ của chúng tôi. Bây giờ khi chúng ta sử dụng bộ điều hợp Foosẽ không được tuần tự hóa nữa Barmà chỉ có id của nó sẽ được lấy và đưa vào JSON. Bây giờ chúng ta có Barbộ điều hợp và chúng ta có thể sử dụng nó để tuần tự hóa Foo. Đây là ý tưởng:

public String getSomething() {
    //getRelevantFoos() is some method that fetches foos from database, and puts them in list
    List<Foo> fooList = getRelevantFoos();

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Bar.class, new BarAdapter());
    Gson gson = gsonBuilder.create();

    String jsonResponse = gson.toJson(fooList);
    return jsonResponse;
}

Một điều nữa cần làm rõ, foo_idkhông bắt buộc và có thể bỏ qua. Mục đích của bộ điều hợp trong ví dụ này là để tuần tự hóa Barvà bằng cách đặt foo_idchúng tôi đã chỉ ra rằng Barcó thể kích hoạt ManyToOnemà không gây Fookích hoạt OneToManylại ...

Câu trả lời dựa trên kinh nghiệm cá nhân, vì vậy hãy bình luận, chứng minh tôi sai, sửa lỗi hoặc mở rộng câu trả lời. Nhưng dù sao tôi hy vọng ai đó sẽ thấy câu trả lời này hữu ích.


Bản thân việc xác định bộ điều hợp để xử lý proecss tuần tự hóa là một cách khác để xử lý sự phụ thuộc theo chu kỳ. Bạn có tùy chọn cho nó mặc dù có các chú thích khác có thể ngăn việc này xảy ra thay vì viết bộ điều hợp.
Sariq Shaikh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.