ترميز YAML للسلسلة المشوهة ، قضايا التسلسل النموذجية
-
05-07-2019 - |
سؤال
لقد عزلت مشكلة مع Ruby on Rails حيث لا يتم حفظ النموذج الذي يحتوي على عمود مسلسل بشكل صحيح.
ما يجري هو تجزئة ، وما يخرج هو سلسلة YAML التي لا يمكن تحليلها بسبب مشكلات التنسيق. أتوقع أن يتمكن المسلسل بشكل صحيح من تخزين واسترداد أي شيء تعطيه ، لذلك يبدو أن هناك خطأ ما.
تم تنسيق السلسلة المزعجة المعنية بشيء من هذا القبيل:
message_text = <<END
X
X
END
yaml = message_text.to_yaml
puts yaml
# =>
# --- |
#
# X
# X
puts YAML.load(yaml)
# => ArgumentError: syntax error on line 3, col 0: ‘X’
إن مزيج الخط الجديد ، والخط الثاني ذو المسافة البادئة ، والخط الثالث غير الموضعي ، يؤدي إلى فشل المحلل المحلل. يبدو أن حذف الخط الفارغ أو المسافة البادئة يعالج المشكلة ، ولكن يبدو أن هذا خطأ في عملية التسلسل. نظرًا لأنه يتطلب مجموعة فريدة من الظروف ، فأنا على استعداد للمراهنة على أن هذه الحالة الغريبة التي لم يتم التعامل معها بشكل صحيح.
تتطلع وحدة YAML التي تشحن مع روبي وتستخدمها القضبان إلى تفويض جزء كبير من المعالجة إلى SYCK ، ومع ذلك يوفر SYCK بعض التلميحات حول كيفية تشفير البيانات التي ترسلها.
في yaml/rubytypes.rb هناك تعريف السلسلة#to_yaml:
class String
def to_yaml( opts = {} )
YAML::quick_emit( is_complex_yaml? ? self : nil, opts ) do |out|
if is_binary_data?
out.scalar( "tag:yaml.org,2002:binary", [self].pack("m"), :literal )
elsif to_yaml_properties.empty?
out.scalar( taguri, self, self =~ /^:/ ? :quote2 : to_yaml_style )
else
out.map( taguri, to_yaml_style ) do |map|
map.add( 'str', "#{self}" )
to_yaml_properties.each do |m|
map.add( m, instance_variable_get( m ) )
end
end
end
end
end
end
يبدو أن هناك فحصًا هناك للسلاسل التي تبدأ بـ ":" ويمكن الخلط بينها كرمز عند إلغاء التالي ، و: يجب أن يكون خيار: Quote2 مؤشراً على الاقتباس أثناء عملية الترميز. لا يبدو أن ضبط هذا التعبير العادي للقبض على الظروف الموضحة أعلاه له أي تأثير على الإخراج ، لذلك آمل أن ينصح شخص أكثر دراية بتنفيذ YAML.
المحلول
نعم ، هذا يبدو وكأنه خطأ في مكتبة C SYCK. لقد راجعت ذلك باستخدام روابط PHP SYCK (V 0.9.3): http://pecl.php.net/package/syck ونفس الخطأ موجود ، مما يشير إلى أنه خطأ في المكتبة بدلاً من مكتبة يامل روبي أو روابط Ruby-SYCK:
// phptestsyck.php
<?php
$message_text = "
X
X
";
syck_load(syck_dump($message_text));
?>
إن تشغيل هذا على CLI يعطي نفس syckexception:
$ php phptestsyck.php
PHP Fatal error: Uncaught exception 'SyckException' with message 'syntax error on line 5, col 0: 'X'' in /.../phptestsyck.php:8
Stack trace:
#0 /.../phptestsyck.php(8): syck_load('--- %YAML:1.0 >...')
#1 {main}
thrown in /.../phptestsyck.php on line 8
لذلك ، أفترض أنك قد تحاول إصلاح Syck نفسها. يبدو أن المكتبة لم يتم تحديثها منذ V0.55 في مايو من عام 2005 (http://rubyforge.org/projects/syck/)، على أية حال.
بالتناوب ، هناك محلل يامل نقي يسمى RBYAML (http://rbyaml.rubyforge.org/) التي نشأت مع jruby التي لا يبدو أن لديها هذا الخطأ:
>> require 'rbyaml'
=> true
>> message_text = <<END
X
X
END
=> "\n X\nX\n"
>> yaml = RbYAML.dump(message_text)
=> "--- "\\n X\\nX\\n"\n"
>> RbYAML.load(yaml)
=> "\n X\nX\n"
>>
أخيرًا ، هل فكرت في تنسيق تسلسل آخر تمامًا؟ مكتبة مارشال روبي لا تحتوي على هذا الخطأ أيضًا وهو أسرع من YAML (انظر http://significantbits.wordpress.com/2008/01/29/yaml-vs-marshal-performance/):
>> message_text = <<END
X
X
END
=> "\n X\nX\n"
>> marshal = Marshal.dump(message_text)
=> "\004\b"\f\n X\nX\n"
>> Marshal.load(marshal)
=> "\n X\nX\n"
نصائح أخرى
عليك أن تتخلى عن السهل serialize
ActivereCord :: طريقة أساسية للقيام بذلك ، ولكن ليس من الصعب استخدام مخطط التسلسل الخاص بك. على سبيل المثال ، لتسلسل بعض الحقل يسمى "person_data":
class Person < ActiveRecord::Base
def person_data
self[:person_data] ? Marshal.load(self[:person_data]) : nil
end
def person_data=(x)
self[:person_data] = Marshal.dump(x)
end
end
## User Person#person_data as normal and it is transparently marshalled
p = Person.find 1
p.person_data = {:color => "blue", :food => "vegetarian"}
(انظر الى هذا موضوع منتدى روبي للمزيد من)