हाइबरनेट द्विदिश मानचित्रण के कारण जसन धारावाहिक में परिपत्र संदर्भ कैसे हल करें?


82

मैं JJ के लिए POJO को क्रमबद्ध करने के लिए एक धारावाहिक लिख रहा हूं लेकिन परिपत्र संदर्भ समस्या में फंस गया। हाइबरनेट द्विदिश में एक से कई संबंध, माता-पिता बच्चे का संदर्भ देते हैं और बच्चे माता-पिता का संदर्भ देते हैं और यहां मेरे धारावाहिक की मृत्यु हो जाती है। (नीचे उदाहरण कोड देखें)
कैसे इस चक्र को तोड़ने के लिए? क्या हम किसी वस्तु के मालिक का पेड़ देख सकते हैं कि क्या वस्तु स्वयं अपने मालिक के पदानुक्रम में कहीं मौजूद है? यह पता लगाने का कोई अन्य तरीका है कि क्या संदर्भ परिपत्र होने जा रहा है? या इस समस्या को हल करने के लिए कोई अन्य विचार?


क्या आप अपने मुद्दे को हल करने में मदद करने के लिए हमारे लिए कुछ कोड में पेस्ट करने का मतलब है?
रसेल

2
यूजीन का एनोटेशन आधारित समाधान ठीक है, लेकिन इस मामले में अतिरिक्त एनोटेशन और एक्सक्लूजनस्ट्रेरी कार्यान्वयन की आवश्यकता नहीं है। बस इसके लिए जावा ' क्षणिक ' कीवर्ड का उपयोग करें । यह मानक जावा ऑब्जेक्ट सीरियलाइजेशन के लिए काम करता है, लेकिन इसके अलावा Gson इसका सम्मान करता है
MeTTeO

जवाबों:


11

क्या JSON में भी द्वि-दिशात्मक संबंध का प्रतिनिधित्व किया जा सकता है? कुछ डेटा प्रारूप कुछ प्रकार के डेटा मॉडलिंग के लिए अच्छे फिट नहीं हैं।

ट्रैवर्सिंग ऑब्जेक्ट ग्राफ़ के साथ काम करते समय चक्र से निपटने के लिए एक विधि यह है कि आपने अब तक देखी गई वस्तुओं (जो पहचान की तुलना का उपयोग करके) को ट्रैक करने के लिए है, अपने आप को एक अनंत चक्र से नीचे आने से रोकने के लिए।


मैंने भी यही किया है और यह काम करता है लेकिन यकीन नहीं होता कि यह सभी मैपिंग परिदृश्यों में काम करेगा। कम से कम अब मैं अच्छी तरह से सेट हूं और अधिक सुंदर विचार रखने पर सोचता रहूंगा
WSK

बेशक वे इसके लिए मूल डेटा प्रकार या संरचना नहीं है। लेकिन XML, JSON, या अधिकांश अन्य डेटा स्वरूपों में किसी भी चीज़ का प्रतिनिधित्व किया जा सकता है।
स्टेक्समैन

4
मैं उत्सुक हूं - आप JSON में एक परिपत्र संदर्भ का प्रतिनिधित्व कैसे करेंगे?
मैट b

1
कई तरीके: ध्यान रखें कि आमतौर पर यह केवल JSON नहीं है, बल्कि JSON और कुछ मेटाडेटा का संयोजन है, सबसे आम तौर पर वर्ग परिभाषाएँ जो आप JSON से / से बांधने के लिए उपयोग करते हैं। JSON के लिए यह सिर्फ इस बात का प्रश्न है कि किसी प्रकार की वस्तु पहचान का उपयोग करें या लिंकेज को फिर से बनाएं (उदाहरण के लिए जैक्सन लिब का एक विशिष्ट तरीका है माता-पिता / बच्चे के संबंध का प्रतिनिधित्व करना)।
स्टैक्मैन मैन 27'10

46

मैं इस सुविधा का उपयोग करके इस तरह के मुद्दे को संभालने के लिए Google JSON पर निर्भर हूं

Serialization और Deserialization से फील्ड को छोड़कर

मान लीजिए कि ए और बी वर्ग के बीच द्वि-दिशात्मक संबंध निम्नानुसार है

public class A implements Serializable {

    private B b;

}

और बी

public class B implements Serializable {

    private A a;

}

अब GsonBuilder का उपयोग करें एक कस्टम Gson वस्तु प्राप्त करने के लिए निम्नानुसार है (सूचना setExclusionStrategies विधि)

Gson gson = new GsonBuilder()
    .setExclusionStrategies(new ExclusionStrategy() {

        public boolean shouldSkipClass(Class<?> clazz) {
            return (clazz == B.class);
        }

        /**
          * Custom field exclusion goes here
          */
        public boolean shouldSkipField(FieldAttributes f) {
            return false;
        }

     })
    /**
      * Use serializeNulls method if you want To serialize null values 
      * By default, Gson does not serialize null values
      */
    .serializeNulls()
    .create();

अब हमारे परिपत्र संदर्भ

A a = new A();
B b = new B();

a.setB(b);
b.setA(a);

String json = gson.toJson(a);
System.out.println(json);

GsonBuilder वर्ग पर एक नज़र डालें


अपनी तरह के सुझाव के लिए आर्थर को धन्यवाद, लेकिन वास्तविक सवाल यह है कि तथाकथित "shouldSkipClass" विधि का निर्माण करने का सबसे अच्छा तरीका क्या है। अभी के लिए मैंने मैट के विचार पर काम किया और अपने मुद्दे को हल किया, लेकिन अभी भी संदेहपूर्ण है, भविष्य में यह समाधान प्रमाणित परिदृश्य में टूट सकता है।
WSK

9
यह उन्हें हटाकर परिपत्र संदर्भों को हल करता है। जेनसन से उत्पन्न मूल डेटा संरचना के पुनर्निर्माण का कोई तरीका नहीं है।
सोतीरियोस डेलिमोलिस

34

जैक्सन 1.6 (रिलीज़ सितंबर 2010) को इस तरह के माता-पिता / बच्चे के संबंध को संभालने के लिए विशिष्ट एनोटेशन-आधारित समर्थन है, http://wiki.fasterxml.com/JacksonFeatureBiDirReferences देखें । ( वेकबैक स्नैपशॉट )

आप निश्चित रूप से पहले से ही अधिकांश JSON प्रोसेसिंग पैकेजों (जैकसन, गन्सन और फ्लेक्स-जोंस का कम से कम समर्थन करते हैं) का उपयोग करके पहले से ही मूल लिंक को क्रमबद्ध रूप से बाहर कर सकते हैं, लेकिन असली चाल यह है कि इसे कैसे वापस रखा जाए (पैरेंट लिंक को फिर से बनाएं)। बस क्रमबद्धता को संभालें। हालाँकि अभी के लिए लगता है कि सिर्फ बहिष्करण आपके लिए काम कर सकता है।

EDIT (अप्रैल 2012): जैक्सन 2.0 अब सही पहचान संदर्भ ( वेबैक स्नैपशॉट ) का समर्थन करता है , इसलिए आप इसे इस तरह भी हल कर सकते हैं।


जब आप दिशा हमेशा एक समान नहीं होती हैं तो यह काम कैसे करें .. मैंने दोनों क्षेत्रों पर दोनों एनोटेशन डालने की कोशिश की, लेकिन यह काम नहीं किया: क्लास ए {@JsonBackReference ("abc") @JsonManagedReference ("xyz") निजी बी बी; } कक्षा B {@JsonManagedReference ("abc") @JsonBackReference ("xyz") निजी ए; }
अजी

ऊपर के अनुसार, Object Id (@JsonIdentityInfo) सामान्य संदर्भ कार्य करने का तरीका है। प्रबंधित / वापस संदर्भों को कुछ दिशाओं की आवश्यकता होती है, इसलिए वे आपके मामले के लिए काम नहीं करेंगे।
स्टेक्समैन

12

इस समस्या को हल करने में, मैंने निम्नलिखित दृष्टिकोण लिया (अपने आवेदन में प्रक्रिया को मानकीकृत करना, कोड को स्पष्ट और पुन: प्रयोज्य बनाना):

  1. उन क्षेत्रों पर उपयोग किए जाने वाले एनोटेशन वर्ग बनाएं जिन्हें आप बाहर करना चाहते हैं
  2. एक वर्ग को परिभाषित करें जो Google के बहिष्करणस्ट्रैटी इंटरफ़ेस को लागू करता है
  3. GsonBuilder (आर्थर के स्पष्टीकरण के समान) का उपयोग करके GSON ऑब्जेक्ट बनाने के लिए एक सरल विधि बनाएं
  4. आवश्यकतानुसार खेतों को बाहर निकाल दें
  5. अपने com.google.gson.Gson ऑब्जेक्ट में क्रमांकन नियम लागू करें
  6. अपनी वस्तु को क्रमबद्ध करें

यहाँ कोड है:

1)

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, ElementType.METHOD})
public @interface GsonExclude {

}

2)

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

public class GsonExclusionStrategy implements ExclusionStrategy{

    private final Class<?> typeToExclude;

    public GsonExclusionStrategy(Class<?> clazz){
        this.typeToExclude = clazz;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return ( this.typeToExclude != null && this.typeToExclude == clazz )
                    || clazz.getAnnotation(GsonExclude.class) != null;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(GsonExclude.class) != null;
    }

}

3)

static Gson createGsonFromBuilder( ExclusionStrategy exs ){
    GsonBuilder gsonbuilder = new GsonBuilder();
    gsonbuilder.setExclusionStrategies(exs);
    return gsonbuilder.serializeNulls().create();
}

4)

public class MyObjectToBeSerialized implements Serializable{

    private static final long serialVersionID = 123L;

    Integer serializeThis;
    String serializeThisToo;
    Date optionalSerialize;

    @GsonExclude
    @ManyToOne(fetch=FetchType.LAZY, optional=false)
    @JoinColumn(name="refobj_id", insertable=false, updatable=false, nullable=false)
    private MyObjectThatGetsCircular dontSerializeMe;

    ...GETTERS AND SETTERS...
}

5)

पहले मामले में, नल को निर्माणकर्ता को आपूर्ति की जाती है, आप एक अन्य वर्ग को बाहर करने के लिए निर्दिष्ट कर सकते हैं - दोनों विकल्प नीचे जोड़े गए हैं

Gson gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(null) );
Gson _gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(Date.class) );

6)

MyObjectToBeSerialized _myobject = someMethodThatGetsMyObject();
String jsonRepresentation = gsonObj.toJson(_myobject);

या, दिनांक ऑब्जेक्ट को बाहर करने के लिए

String jsonRepresentation = _gsonObj.toJson(_myobject);

1
यह चाइल्ड ऑब्जेक्ट को प्रोसेस होने से रोकता है, क्या मैं चाइल्ड
जोंस को

3

यदि आप जैक्सन का उपयोग क्रमबद्ध करने के लिए कर रहे हैं, तो बस अपने द्वि-दिशात्मक मानचित्रण पर @JsonBackReference लागू करें यह परिपत्र संदर्भ समस्या को हल करेगा।

नोट: @JsonBackReference का उपयोग अनंत पुनरावृत्ति (StackOverflowError) को हल करने के लिए किया जाता है


@JsonIgnoreJpaRepositoryसंपत्ति का नक्शा बनाने में मेरी नाकाम कोशिश की, लेकिन @JsonBackReferenceपरिपत्र संदर्भ को हल किया और अभी भी समस्याग्रस्त विशेषता के लिए उचित मानचित्रण की अनुमति दी
Bramastic

2

आर्थर के समान एक समाधान का setExclusionStrategiesउपयोग किया, लेकिन इसके बजाय मैंने इस्तेमाल किया

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

और @Exposeजिन क्षेत्रों में मुझे जोसन की आवश्यकता है, उन क्षेत्रों के लिए गन्स एनोटेशन का उपयोग किया जाता है , अन्य क्षेत्रों को बाहर रखा गया है।


धन्यवाद दोस्त। यह मेरे लिए काम किया है। लंबे शोध के बाद आखिरकार मुझे इस त्रुटि से निकलने का एक रास्ता मिल गया।
kepy97

2

यदि आप स्प्रिंग बूट का उपयोग कर रहे हैं, तो जैक्सन परिपत्र / द्विदिश डेटा से प्रतिक्रिया बनाते समय त्रुटि फेंकता है, इसलिए उपयोग करें

@JsonIgnoreProperties

परिपत्रता को अनदेखा करना

At Parent:
@OneToMany(mappedBy="dbApp")
@JsonIgnoreProperties("dbApp")
private Set<DBQuery> queries;

At child:
@ManyToOne
@JoinColumn(name = "db_app_id")
@JsonIgnoreProperties("queries")
private DBApp dbApp;

1

यदि आप जावास्क्रिप्ट का उपयोग कर रहे हैं, तो उस पद्धति के replacerपैरामीटर का उपयोग करके एक बहुत ही आसान समाधान है JSON.stringify()जहां आप डिफ़ॉल्ट क्रमिक व्यवहार को संशोधित करने के लिए एक फ़ंक्शन पास कर सकते हैं।

यहां बताया गया है कि आप इसका उपयोग कैसे कर सकते हैं। चक्रीय ग्राफ में 4 नोड्स के साथ नीचे दिए गए उदाहरण पर विचार करें।

// node constructor
function Node(key, value) {
    this.name = key;
    this.value = value;
    this.next = null;
}

//create some nodes
var n1 = new Node("A", 1);
var n2 = new Node("B", 2);
var n3 = new Node("C", 3);
var n4 = new Node("D", 4);

// setup some cyclic references
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n1;

function normalStringify(jsonObject) {
    // this will generate an error when trying to serialize
    // an object with cyclic references
    console.log(JSON.stringify(jsonObject));
}

function cyclicStringify(jsonObject) {
    // this will successfully serialize objects with cyclic
    // references by supplying @name for an object already
    // serialized instead of passing the actual object again,
    // thus breaking the vicious circle :)
    var alreadyVisited = [];
    var serializedData = JSON.stringify(jsonObject, function(key, value) {
        if (typeof value == "object") {
            if (alreadyVisited.indexOf(value.name) >= 0) {
                // do something other that putting the reference, like 
                // putting some name that you can use to build the 
                // reference again later, for eg.
                return "@" + value.name;
            }
            alreadyVisited.push(value.name);
        }
        return value;
    });
    console.log(serializedData);
}

बाद में, आप क्रमिक डेटा को पार्स करके और nextवास्तविक वस्तु को इंगित करने के लिए संपत्ति को संशोधित करके चक्रीय संदर्भों के साथ वास्तविक वस्तु को आसानी से फिर से बना सकते हैं यदि यह @इस उदाहरण में एक जैसे नाम के साथ संदर्भ का उपयोग कर रहा है ।


1

इस तरह से मैंने अपने मामले में इसे हल कर लिया। यह कम से कम Gson और जैक्सन के साथ काम करता है।

private static final Gson gson = buildGson();

private static Gson buildGson() {
    return new GsonBuilder().addSerializationExclusionStrategy( getExclusionStrategy() ).create();  
}

private static ExclusionStrategy getExclusionStrategy() {
    ExclusionStrategy exlStrategy = new ExclusionStrategy() {
        @Override
        public boolean shouldSkipField(FieldAttributes fas) {
            return ( null != fas.getAnnotation(ManyToOne.class) );
        }
        @Override
        public boolean shouldSkipClass(Class<?> classO) {
            return ( null != classO.getAnnotation(ManyToOne.class) );
        }
    };
    return exlStrategy;
} 

0

यह त्रुटि तब हो सकती है जब आपके पास दो वस्तुएं हों:

class object1{
    private object2 o2;
}

class object2{
    private object1 o1;
}

क्रमांकन के लिए GSon का उपयोग करने के साथ, मुझे यह त्रुटि मिली है:

java.lang.IllegalStateException: circular reference error

Offending field: o1

इसे हल करने के लिए, बस कुंजी शब्द क्षणिक जोड़ें:

class object1{
    private object2 o2;
}

class object2{
    transient private object1 o1;
}

जैसा कि आप यहाँ देख सकते हैं: जावा में क्षणिक क्षेत्र क्यों हैं?

जावा में क्षणिक कीवर्ड का उपयोग यह इंगित करने के लिए किया जाता है कि किसी क्षेत्र को क्रमबद्ध नहीं किया जाना चाहिए।



0

यदि आप JSON में जावा वर्ग को परिवर्तित करने के लिए GSON का उपयोग करते हैं, तो आप उन क्षेत्रों से बच सकते हैं जो परिपत्र संदर्भ और इन्फिनिटिव लूप का कारण बनते हैं, आपको केवल एनोटेशन @ एक्सपोज करना होगा उन फ़ील्ड में जो आप JSON में दिखाना चाहते हैं, और बिना फ़ील्ड एनोटेशन @ एक्सपोज JSON में दिखाई नहीं देता है।

परिपत्र संदर्भ उदाहरण के लिए प्रकट होता है यदि हम वर्ग रूट के क्षेत्र मार्गों के साथ वर्ग उपयोगकर्ता को क्रमबद्ध करने का प्रयास करते हैं, और वर्ग रूट में उपयोगकर्ता का वर्ग उपयोगकर्ता है, तो GSON वर्ग उपयोगकर्ता को क्रमबद्ध करने का प्रयास करते हैं और जब मार्गों को क्रमबद्ध करने का प्रयास करते हैं, वर्ग रूट को क्रमबद्ध करें और कक्षा में रूट उपयोगकर्ता को क्रमबद्ध करने का प्रयास करें, और फिर से वर्ग उपयोगकर्ता को क्रमबद्ध करने का प्रयास करें, एक परिपत्र संदर्भ है जो कि इनफिनिटिव लूप को उत्तेजित करता है। मैं वर्ग उपयोगकर्ता और मार्ग दिखाता हूं जिसमें उल्लेख किया गया है।

import com.google.gson.annotations.Expose;

कक्षा उपयोगकर्ता

@Entity
@Table(name = "user")
public class User {
    
@Column(name = "name", nullable = false)
@Expose
private String name;

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
private Set<Route> routes;

@ManyToMany(fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinTable(name = "like_", joinColumns = @JoinColumn(name = "id_user"),
        inverseJoinColumns = @JoinColumn(name = "id_route"),
        foreignKey = @ForeignKey(name = ""),
        inverseForeignKey = @ForeignKey(name = ""))
private Set<Route> likes;

क्लास रूट

  @Entity
  @Table(name = "route")
  public class Route {
      
  @ManyToOne()
  @JoinColumn(nullable = false, name = "id_user", foreignKey = 
  @ForeignKey(name = "c"))    
  private User user;

इनफ़िनिटिव लूप से बचने के लिए, हम एनोटेशन @ का उपयोग करते हैं जो GSON की पेशकश करते हैं।

मैं प्रारूप JSON में वर्ग उपयोगकर्ता के साथ क्रमबद्ध का परिणाम दिखाता हूं।

{
    "name": "ignacio"  
}

हम देख सकते हैं कि क्षेत्र मार्ग और पसंद केवल JSON प्रारूप में मौजूद नहीं है। इस वजह से, परिपत्र संदर्भ से बचा जाता है।

यदि हम इसका उपयोग करना चाहते हैं, तो हमें एक विशिष्ट तरीके से एक वस्तु जीएसओएन बनाना होगा।

Gson converterJavaToJson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();

अंत में, हम बनाए गए GSON का उपयोग करके हाइबरनेट उपयोगकर्ता के मॉडल के जावा वर्ग को रूपांतरित करते हैं।

 User user = createUserWithHibernate();
 String json = converterJavaToJson.toJson(user);

-3

उत्तर संख्या 8 बेहतर है, मुझे लगता है कि अगर आपको पता है कि किस क्षेत्र में कोई त्रुटि है, तो आप केवल शून्य में हल को सेट करें और हल करें।

List<RequestMessage> requestMessages = lazyLoadPaginated(first, pageSize, sortField, sortOrder, filters, joinWith);
    for (RequestMessage requestMessage : requestMessages) {
        Hibernate.initialize(requestMessage.getService());
        Hibernate.initialize(requestMessage.getService().getGroupService());
        Hibernate.initialize(requestMessage.getRequestMessageProfessionals());
        for (RequestMessageProfessional rmp : requestMessage.getRequestMessageProfessionals()) {
            Hibernate.initialize(rmp.getProfessional());
            rmp.setRequestMessage(null); // **
        }
    }

कोड को पठनीय बनाने के लिए एक बड़ी टिप्पणी को टिप्पणी // **से नीचे ले जाया जाता है।

java.lang.StackOverflowError [अनुरोध प्रसंस्करण विफल रहा; नेस्टेड अपवाद है org.springframework.http.converter.HttpMessageNotWritableException: JSON नहीं लिख सकता: अनंत पुनरावर्तन (StackOverflowError) (श्रृंखला के माध्यम से: com.service.pegazo.bo.RequestMessageProfessional ["requestMessage") -। bo.RequestMessage ["requestMessageProfessionals"]


1
कोई "उत्तर संख्या 8" नहीं है, तो आप संदर्भित उत्तर के लेखक का नाम देना बेहतर होगा। आपके द्वारा यहां पोस्ट किया गया पाठ अपठनीय था, कृपया देखें कि कैसे उत्तर पोस्ट किए गए हैं और उन्हें बड़े करीने से बिछाने का प्रयास करें। अंत में मुझे समझ नहीं आया कि यह मूल प्रश्न का उत्तर कैसे देता है। उत्तर को समझाने के लिए कृपया अधिक विवरण जोड़ें।
AdrianHHH

-11

उदाहरण के लिए, ProductBean को serialBean मिला है। मानचित्रण द्वि-दिशात्मक संबंध होगा। यदि हम अब उपयोग करने का प्रयास करते हैं gson.toJson(), तो यह परिपत्र संदर्भ के साथ समाप्त हो जाएगा। उस समस्या से बचने के लिए, आप इन चरणों का पालन कर सकते हैं:

  1. डेटा स्रोत से परिणाम प्राप्त करें।
  2. सूची में दर्ज करें और सुनिश्चित करें कि धारावाहिक शून्य नहीं है, और फिर
  3. सेट productBean.serialBean.productBean = null;
  4. फिर उपयोग करने का प्रयास करें gson.toJson();

इससे समस्या का समाधान होना चाहिए

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.