2012-10-10 25 views
9

पास करके रूबी वाईएएमएल पार्सर मैं एक एप्लीकेशन पर काम कर रहा हूं जो एक वाईएएमएल फ़ाइल से इनपुट लेता है, उन्हें ऑब्जेक्ट्स में पार्स करता है, और चलिए उन्हें अपनी बात करते हैं। मेरे पास अब एकमात्र समस्या है, यह है कि वाईएएमएल पार्सर ऑब्जेक्ट को "प्रारंभिक" विधि को अनदेखा कर रहा है। मैं किसी भी इंस्टेंस चर को भरने के लिए कन्स्ट्रक्टर पर गिन रहा था, वाईएएमएल फ़ाइल में डिफ़ॉल्ट के साथ कमी थी, साथ ही क्लास वेरिएबल में कुछ चीजें स्टोर भी थीं।कन्स्ट्रक्टर

class Test 

    @@counter = 0 

    def initialize(a,b) 
     @a = a 
     @b = b 

     @a = 29 if @b == 3 

     @@counter += 1 
    end 

    def self.how_many 
     p @@counter 
    end 

    attr_accessor :a,:b 

end 

require 'YAML' 

a = Test.new(2,3) 
s = a.to_yaml 
puts s 
b = YAML::load(s) 
puts b.a 
puts b.b 
Test.how_many 

puts "" 

c = Test.new(4,4) 
c.b = 3 
t = c.to_yaml 
puts t 
d = YAML::load(t) 
puts d.a 
puts d.b 
Test.how_many 

मैं उत्पादन के लिए ऊपर की उम्मीद है |: यहाँ एक उदाहरण है

--- !ruby/object:Test 
a: 29 
b: 3 
29 
3 
2 

--- !ruby/object:Test 
a: 4 
b: 3 
29 
3 
4 

इसके बजाय मुझे मिल गया:

--- !ruby/object:Test 
a: 29 
b: 3 
29 
3 
1 

--- !ruby/object:Test 
a: 4 
b: 3 
4 
3 
2 

मुझे समझ नहीं आता कि यह कैसे बिना इन वस्तुओं बनाता है उनकी परिभाषित प्रारंभिक विधि का उपयोग कर। मैं यह भी सोच रहा हूं कि क्या प्रारंभिक विधि का उपयोग करने के लिए पार्सर को मजबूर करने के लिए वैसे भी है।

+1

संभावित डुप्लिकेट: http: // stackoverflow।कॉम/प्रश्न/1823386/कॉलिंग-प्रारंभिक-कब-लोडिंग-ए-ऑब्जेक्ट-सीरियलाइज्ड-साथ-yaml –

+0

वह पोस्ट उपयोगी है, हालांकि यह मेरी समस्या को पूरी तरह से हल नहीं करता है। वाईएएमएल धारा जो मैं पार्सिंग कर रहा हूं वह एक वस्तु से अधिक जटिल है, यह कई वस्तुएं हैं, जिनमें से कुछ दूसरों से बना है। – clementine

+0

क्षमा करें, बस कोशिश कर रहा है। शायद यह अधिक उपयोगी है: _why_ 'YAML :: load' को खोजने के लिए _not_ कॉल 'प्रारंभ करें', स्रोत की जांच करें। : पी या शायद हम एक उत्तर देने वाले के लिए इंतजार कर सकते हैं जो अधिक जानकारी जानता है। मैंने आपकी स्क्रिप्ट में 'd.class == c.class' डालने का प्रयास किया और इसे सत्य मान लिया। तो अपने प्रश्न पर +1। –

उत्तर

9

YAML से एक वस्तु initialize विधि का उपयोग नहीं करता deserializing क्योंकि सामान्य रूप से ऑब्जेक्ट्स इवेंट वैरिएबल (जो डिफ़ॉल्ट यमल सीरियलाइजेशन स्टोर्स है) और पैरामीटर initialize के बीच कोई पत्राचार नहीं है।

उदाहरण के लिए, एक initialize कि इस तरह दिखता है के साथ एक वस्तु (कोई अन्य उदाहरण चर के साथ) पर विचार करें:

def initialize(param_one, param_two) 
    @a_variable = some_calculation(param_one, param_two) 
end 

अब जब इस का एक उदाहरण deserialized है, YAML प्रोसेसर @a_variable के लिए एक मूल्य है , लेकिन initialize विधि के लिए दो पैरामीटर की आवश्यकता है, इसलिए यह इसे कॉल नहीं कर सकता है। यहां तक ​​कि यदि आवृत्ति चर की संख्या initialize पर पैरामीटर की संख्या से मेल खाती है, तो यह आवश्यक नहीं है कि वे किस मामले से मेल खाते हों, और भले ही वे प्रोसेसर को आदेश नहीं जानते कि वे initialize पर भेजे गए हैं।

यारिल को रूबी ऑब्जेक्ट को क्रमबद्ध करने और deserializing करने के लिए डिफ़ॉल्ट प्रक्रिया serialization के दौरान सभी आवृत्ति चर (उनके नामों के साथ) लिखना है, तो जब वर्ग के एक नए उदाहरण आवंटित deserializing और बस इस पर एक ही उदाहरण चर सेट नया उदाहरण

बेशक कभी-कभी आपको इस प्रक्रिया के अधिक नियंत्रण की आवश्यकता होती है। यदि आप साइको यमल प्रोसेसर (जो रूबी 1.9.3 में डिफ़ॉल्ट है) का उपयोग कर रहे हैं तो आपको encode_with (क्रमबद्धता के लिए) या init_with (deserialization के लिए) विधियों को लागू करना चाहिए।

क्रमबद्ध करने के लिए, यदि coder object पास कर रहा है, तो साइप्रस encode_with किसी ऑब्जेक्ट की विधि को कॉल करेगा। यह ऑब्जेक्ट आपको यह निर्दिष्ट करने की अनुमति देता है कि वस्तु को यमल में कैसे प्रदर्शित किया जाना चाहिए - आम तौर पर आप इसे केवल हैश की तरह मानते हैं।

अक्रमांकन के लिए, साइक init_with विधि कॉल करेगा कि क्या यह बजाय ऊपर वर्णित डिफ़ॉल्ट प्रक्रिया का उपयोग कर, फिर से एक coder वस्तु उत्तीर्ण होने की अपनी वस्तु पर मौजूद है। इस बार coder में यमल में वस्तुओं के प्रतिनिधित्व के बारे में जानकारी होगी।

ध्यान दें कि आपको दोनों विधियां प्रदान करने की आवश्यकता नहीं है, यदि आप चाहें तो केवल एक ही प्रदान कर सकते हैं। यदि आप दोनों प्रदान करते हैं, तो coder ऑब्जेक्ट जो आपको init_with में पारित किया गया है, वह आवश्यक रूप से उसी विधि के बाद encode_with पर पारित होने जैसा ही होगा।

उदाहरण के तौर पर, किसी ऑब्जेक्ट पर विचार करें जिसमें कुछ उदाहरण चर हैं जिन्हें दूसरों से गणना की जाती है (शायद बड़ी गणना से बचने के लिए अनुकूलन के रूप में), लेकिन यमल को क्रमबद्ध नहीं किया जाना चाहिए।

class Foo 

    def initialize(first, second) 
    @first = first 
    @second = second 
    @calculated = expensive_calculation(@first, @second) 
    end 

    def encode_with(coder) 
    # @calculated shouldn’t be serialized, so we just add the other two. 
    # We could provide different names to use in the Yaml here if we 
    # wanted (as long as the same names are used in init_with). 
    coder['first'] = @first 
    coder['second'] = @second 
    end 

    def init_with(coder) 
    # The Yaml only contains values for @first and @second, we need to 
    # recalculate @calculated so the object is valid. 
    @first = coder['first'] 
    @second = coder['second'] 
    @calculated = expensive_calculation(@first, @second) 
    end 

    # The expensive calculation 
    def expensive_calculation(a, b) 
    ... 
    end 
end 

जब आप YAML को इस वर्ग का एक उदाहरण डंप, यह कुछ इस तरह दिखेगा, calculated मूल्य के बिना:

--- !ruby/object:Foo 
first: 1 
second: 2 

जब आप इस YAML वापस रूबी में लोड, निर्मित ऑब्जेक्ट होगा @calculated इंस्टेंस वैरिएबल सेट है।

क्या आप init_with के भीतर से सकता है कॉल initialize चाहता था, लेकिन मुझे लगता है कि यह बेहतर होगा एक नई वर्ग के उदाहरण आरंभ, और YAML से एक मौजूदा उदाहरण deserializing के बीच स्पष्ट अलगाव रखने के लिए तो । मैं सामान्य तर्क को उन विधियों में निकालने की अनुशंसा करता हूं जिन्हें बदले में

+0

मुझे इस बारे में स्पष्टीकरण पसंद है कि यह प्रारंभिक विधि का उपयोग क्यों नहीं करता है, जो मुझे समझ में आता है। प्रश्न तब होता है जब वाईएएमएल से लोड होने पर ऑब्जेक्ट कैसे बनाया जाता है, इस व्यवहार को कैसे बदला जाए। आपके उत्तर के ऊपर मेरे पास दो हैं जो ऑब्जेक्ट.to_yaml और Object.allocate का उपयोग करने का सुझाव देते हैं। तो यह कौन है ?? Object.init_with, Object.allocate, या Object.to_yaml? मुझे लगता है कि आपको इस पर दस्तावेज मिला है, क्या आप मुझे लिंक भेज सकते हैं? – clementine

+0

@ रूबिगेम्स के लिए एपीआई से 'from_yaml' लिंक का उपयोग करने का सुझाव देने वाला उत्तर, यह यमल का हिस्सा नहीं है। अन्य स्टैकओवरफ्लो प्रश्न के उत्तर में, 'from_yaml' का उपयोग सिर्फ सादे विधि के रूप में किया जाता है, यह शेष यम लोडिंग/डंपिंग के साथ एकीकृत नहीं होगा। 'आवंटित' काफी कम स्तर की सामग्री है, मैं इसके साथ हस्तक्षेप से बचने की सलाह दूंगा। – matt

+0

@clementine साइको के लिए प्रलेखन बहुत खराब है, लेकिन मैं उस स्रोत के लिए एक लिंक प्रदान कर सकता हूं जहां 'init_with' की जांच की जाती है और डिफ़ॉल्ट व्यवहार होता है यदि यह वहां नहीं है: https://github.com/tenderlove/psych /blob/v1.3.4/lib/psych/visitors/to_ruby.rb#L291-300। – matt

1

from_yaml(input)

YAML फ़ाइलों के लिए विशेष लोडर। जब Specification ऑब्जेक्ट को वाईएएमएल फ़ाइल से लोड किया जाता है, तो यह सामान्य रूबी ऑब्जेक्ट प्रारंभिक दिनचर्या (प्रारंभिक) को छोड़ देता है। यह विधि इसके लिए तैयार होती है और विभिन्न आयु के रत्नों से संबंधित है।

इनपुट कुछ भी हो सकता है जो YAML.load() स्वीकार करता है: स्ट्रिंग या आईओ।

यही कारण है कि जब आपने YAML::Load निष्पादित किया था तो आरंभिक विधि नहीं चल रही थी।

+0

अच्छा खोज। क्या इसका मतलब है कि मैं इसे से_याम क्लास विधि ओवरराइड करके हल कर सकता हूं ?, इस जवाब में: http://stackoverflow.com/questions/1823386/calling-initialize-when-loading-an-object-serialized-with-yaml < - मेरी समस्या यह थी कि यह एक YAML स्ट्रीम से एक ऑब्जेक्ट लोड कर रहा था। मैं जिस yaml फ़ाइल को लोड कर रहा हूं वह ऑब्जेक्ट्स का एक जटिल विरासत है, जिसमें से सभी को डिफ़ॉल्ट कन्स्ट्रक्टर – clementine

+0

से गुजरना होगा, आप [YAML :: load_documents] (http://yaml4r.sourceforge.net/doc/page/ का उपयोग कर सकते हैं loading_yaml_documents.htm) इसे पूरा करने के लिए। –

+0

कोई भी आपको समझ में नहीं आता है ... यह एक ही फ़ाइल में एकाधिक yaml धारा नहीं है। यह एक यमल धारा है जो रचना में वस्तुओं के पदानुक्रम का वर्णन करती है। तो उदाहरण के लिए, मेरे पास एक ऑब्जेक्ट पेड़ हो सकता है जिसमें फल, पत्तियां, शायद एक पक्षी घोंसला हो, जिनमें से सभी को रूबी ऑब्जेक्ट में अनसुलझा करने की आवश्यकता है – clementine

3

यदि आप केवल @ -स्टाइल आवृत्ति चर (संकलित एक्सटेंशन से नहीं हैं और Struct-स्टाइल नहीं) का उपयोग करते हैं, तो निम्न कार्य करना चाहिए। YAML उस वर्ग का उदाहरण लोड करते समय allocate क्लास विधि को कॉल करने लगता है, भले ही इंस्टेंस किसी अन्य ऑब्जेक्ट के सदस्य के रूप में घोंसला हो। तो हम allocate को फिर से परिभाषित कर सकते हैं। उदाहरण:

class Foo 
    attr_accessor :yaml_flag 
    def self.allocate 
    super.tap {|o| o.instance_variables.include?(:@yaml_flag) or o.yaml_flag = true } 
    end 
end 
class Bar 
    attr_accessor :foo, :yaml_flag 
    def self.allocate 
    super.tap {|o| o.instance_variables.include?(:@yaml_flag) or o.yaml_flag = true } 
    end 
end 

>> bar = Bar.new 
=> #<Bar:0x007fa40ccda9f8> 
>> bar.foo = Foo.new 
=> #<Foo:0x007fa40ccdf9f8> 
>> [bar.yaml_flag, bar.foo.yaml_flag] 
=> [nil, nil] 
>> bar_reloaded = YAML.load YAML.dump bar 
=> #<Bar:0x007fa40cc7dd48 @foo=#<Foo:0x007fa40cc7db90 @yaml_flag=true>, @yaml_flag=true> 
>> [bar_reloaded.yaml_flag, bar_reloaded.foo.yaml_flag] 
=> [true, true] 

# won't overwrite false 
>> bar.foo.yaml_flag = false 
=> false 
>> bar_reloaded = YAML.load YAML.dump bar 
=> #<Bar:0x007fa40ccf3098 @foo=#<Foo:0x007fa40ccf2f08 @yaml_flag=false>, @yaml_flag=true> 
>> [bar_reloaded.yaml_flag, bar_reloaded.foo.yaml_flag] 
=> [true, false] 

# won't overwrite nil 
>> bar.foo.yaml_flag = nil 
=> nil 
>> bar_reloaded = YAML.load YAML.dump bar 
=> #<Bar:0x007fa40cd73518 @foo=#<Foo:0x007fa40cd73360 @yaml_flag=nil>, @yaml_flag=true> 
>> [bar_reloaded.yaml_flag, bar_reloaded.foo.yaml_flag] 
=> [true, nil] 

मैं जानबूझकर tap ब्लॉक में एक o.nil? जांच से बचा क्योंकि nil वास्तव में एक सार्थक मूल्य है कि आप के ऊपर लिख नहीं करना चाहते हो सकता है।

एक अंतिम चेतावनी: allocate तीसरे पक्ष के पुस्तकालयों (या अपने कोड से) द्वारा उपयोग किया जा सकता है, और आप उन मामलों में सदस्यों को सेट नहीं करना चाहेंगे। यदि आप आवंटन को प्रतिबंधित करना चाहते हैं, तो केवल यम लोडिंग के लिए, आपको कुछ और नाजुक और जटिल करना होगा जैसे कि आवंटन विधि में caller स्टैक को जांचें ताकि यह देखने के लिए कि यम इसे कॉल कर रहा है या नहीं।

मैं रूबी 1.9.3 (साइक के साथ) पर हूँ और ढेर के शीर्ष इस (पथ का पहला भाग निकाल दिया) की तरह दिखता है:

psych/visitors/to_ruby.rb:274:in `revive'", 
psych/visitors/to_ruby.rb:219:in `visit_Psych_Nodes_Mapping'", 
psych/visitors/visitor.rb:15:in `visit'", 
psych/visitors/visitor.rb:5:in `accept'", 
psych/visitors/to_ruby.rb:20:in `accept'", 
psych/visitors/to_ruby.rb:231:in `visit_Psych_Nodes_Document'", 
psych/visitors/visitor.rb:15:in `visit'", 
psych/visitors/visitor.rb:5:in `accept'", 
psych/visitors/to_ruby.rb:20:in `accept'", 
psych/nodes/node.rb:35:in `to_ruby'", 
psych.rb:128:in `load'", 
+0

मैं ऑब्जेक्ट.tap से परिचित नहीं हूं, लेकिन धन्यवाद, यह बिल्कुल ठीक दिखता है मुझे इसकी तलाश थी। – clementine

+0

@clementine 'tap' वास्तव में सुविधाजनक है। देखें [रूबी-डॉक्टर] (http://ruby-doc.org/core-1.9.3/Object.html#method-i-tap)। यह आपको किसी ऑब्जेक्ट को किसी चर को असाइन किए बिना चीजों को करने देता है। – Kelvin

संबंधित मुद्दे