2012-09-26 16 views
5

Ich habe Probleme mit Daten zwischen Perl und Ruby über YAML ausgetauscht werden. Ich habe einige Werte, die wie Nummer: Nummer aussehen, z. B. 1:16.YAML-Datenaustausch Probleme zwischen Perl und Ruby

Perls YAML-Bibliotheken (Tiny und XS) codieren dies als 1:16 ohne Anführungszeichen. Rubys YAML-Bibliothek (Psych) interpretiert dies nicht als String, sondern wird stattdessen zum Fixnum-Wert 4560. Ich kann nicht herausfinden, wie man dieses Umwandlungsproblem auf beiden Seiten beheben kann.

Jeder Wert in der YAML für meinen Anwendungsfall sollte ein Objekt oder eine Zeichenfolge sein. Also könnte ich der Perl YAML-Bibliothek sagen, alle Werte zu zitieren, wenn eine solche Option existiert. Oder gibt es eine Möglichkeit, der Ruby YAML-Bibliothek mitzuteilen, alle Werte als Zeichenfolgen zu interpretieren? Irgendwelche Ideen?

Das Ändern der Sprache auf beiden Seiten ist logistisch keine Option.

Perl:

use YAML::XS qw(DumpFile); 
my $foo={'abc'=>'1:16'}; 
DumpFile('test.yaml',$foo); 

Rubin:

require('yaml') 
foo=YAML.load_file('test.yaml') 
puts(foo['abc']) 

Der Code Ruby 4560 drucken. Einer der Kommentare herausgefunden, wie Sie 4560 von 1:16 bekommen, es ist 1 Stunde, 16 Minuten in Sekunden umgewandelt. Äh, okay.

+1

Können Sie einige Beispiel-YAML-Daten zusammen mit dem Ruby- und Perl-Code, den Sie zum Entschlüsseln verwenden, angeben? – Schwern

+3

4560 ist die Anzahl der Sekunden in einer Stunde und 16 Minuten (unter der Annahme, keine Schaltsekunden). – ikegami

+1

Wie sieht die generierte YAML-Datei aus? – bta

Antwort

5

Nach dem Yaml 1.1 spec, 1:16 eine ganze Zahl in sexagesimal (Basis 60) Format.

Siehe auch http://yaml.org/type/int.html, die sagt:

Verwendung „:“ ermöglicht es ganze Zahlen in der Basis 60, die für die Zeit und Winkelwerte bequem ausdrücken.

Der Yaml Parser in Ruby enthalten, Psych, recognises this format and converts the value into an integer (zu Unrecht, shoud 1:16 71 sein - der Code Psych scheint zu asume, dass alle diese Werte in Form a:b:c sein wird, aber die Regex erzwingt nicht das). Der Perl-Emitter (zumindest YAML :: XS, den ich getestet habe) erkennt dieses Format nicht, daher zitiert er die Zeichenkette nicht beim Schreiben der Datei. YAML :: XS tut erkennen und zitieren einige Ganzzahlen, aber nicht alle. YAML :: XS erkennt auch nicht viele andere Formate (z. B. Daten), die Psych tut.

(Es scheint, dass das sexagesimal Format has been removed from the Yaml 1.2 spec.)

Psych ziemlich viel Flexibilität in seiner Analyse ermöglicht - YAML.load_file ist nur eine einfache Schnittstelle für die gemeinsamen Anwendungsfälle.

Sie könnten die parse Methoden von Psych verwenden, um eine Baumdarstellung des yaml zu erstellen, und diese dann in eine Ruby-Datenstruktur mit einer benutzerdefinierten ScalarScanner konvertieren (das Objekt, das Zeichenfolgen bestimmter Formate in den entsprechenden Ruby-Typ konvertiert). :

require('yaml') 

class MyScalarScanner < Psych::ScalarScanner 
    def tokenize string 
    #this is the same regexp as Psych uses to detect base 60 ints: 
    return string if string =~ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/ 
    super 
    end 
end 

tree = YAML::parse_file 'test.yaml' 
foo = Psych::Visitors::ToRuby.new(MyScalarScanner.new).accept tree 

Dies ist im Grunde der gleiche Prozess, wenn Sie YAML.load_file verwenden, außer dass es nutzt die angepasste Scanner Klasse auftritt. Eine ähnliche Alternative wäre, ScalarScanner zu öffnen und die tokenize Methode durch die angepasste zu ersetzen. Dies würde ermöglichen es Ihnen, die einfachere load_file Schnittstelle zu verwenden, aber mit den üblichen Warnungen über Klassen Affe Patchen:

class Psych::ScalarScanner 
    alias :orig_tokenize :tokenize 
    def tokenize string 
    return string if string =~ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/ 
    orig_tokenize string 
    end 
end 

foo = YAML.load_file 'test.yaml' 

Beachten Sie, dass diese Beispiele berücksichtigen ausschließlich die Werte mit einem Format wie 1:16. Je nachdem, was Ihr Perl-Programm ausgibt, müssen Sie möglicherweise auch andere Muster überschreiben. Eine besonders, die Sie vielleicht betrachten möchten, ist Sexagesimal Floats (z. B. 1:16.44).

-4

Ruby interpretiert alle YAML-Einträge als Zeichenfolgen, es sei denn, sie passen a handful of special formats. Der Eintrag 1:16 sieht so aus, als ob er für eine bestimmte Zeit dem speziellen Format entspricht, also interpretiert Ruby ihn falsch.

Sie müssen Ruby zwingen, das Feld als Zeichenfolge zu interpretieren. Es gibt zwei Möglichkeiten, dies zu tun. Eine der folgenden YAML Ausgänge sollten Sie das Ergebnis geben, dass Sie wollen:

abc: !str 1:16 
abc: '1:16' 

diese Ausgabe erzeugen, versuchen Sie die folgenden Perl-Code:

my $foo={'abc'=>'!str 1:16'}; 
my $foo={'abc'=>"'1:16'"}; 

Update: Ich konnte passieren Daten zwischen Perl und Ruby mit dem folgenden Code:

Perl:

use YAML::XS qw(DumpFile); 
my $foo={'abc'=>'1:16'}; 
DumpFile('test.yaml',$foo); 

Rubin:

require 'yaml' 
foo=YAML.parse_file('test.yaml') 
foo['abc'].value 
=> "1:16" 
foo['abc'].value.class 
=> String 

Das Ergebnis ist ein wenig komplizierter als die einfache Hash zu verwenden, die load_file zurückkehrt, aber es sieht aus wie es zumindest ist wie erwartet Parsen der Datei.

+0

Das erzeugt korrekt 'abc:'! Str 1: 16'' und 'abc: '' '1:16' '''. Ich glaube, keiner wird das richtige Ergebnis in Ruby produzieren. – ikegami

+0

'1: 16' stimmt nicht mit den Formaten überein, die in dem Dokument aufgeführt sind, das Sie verknüpft haben. – ikegami

+0

@ ikegami- Es passt das Format für eine einfache Zeit, obwohl die begrenzten Beispiele auf dieser Seite komplexere Beispiele verwenden. Ruby hat viele mögliche Ausgabeformatoptionen für Datums- und Zeitobjekte und es scheint, dass der YAML-Parser alles anzeigt, was möglicherweise ein Datum sein könnte. Meine Perl-Skills sind extrem rostig, daher musst du sie vielleicht optimieren, um die YAML-Ausgabe in dem von mir aufgelisteten Format zu erhalten. – bta

1

Es gibt einen Fehler im Parser, den Sie verwenden. Es scheint zu denken, 1:16 ist eine Art Zeit (seit 4560 ist die Anzahl der Sekunden in einer Stunde und 16 Minuten), aber ich finde nichts, das diese Interpretation bestätigt.

Die beste Lösung wäre, einen Parser zu verwenden, der nicht fehlerhaft ist.

  • libyaml, verwendet von YAML :: XS, hat angeblich Ruby Bindings.
  • libsyck, von YAML :: Syck verwendet, hat angeblich Ruby Bindings.
  • Eine Alternative besteht darin, YAML zu generieren, wo die Strings immer in Anführungszeichen stehen (oder zumindest dann, wenn sie als Zeit behandelt werden).

    YAML::Syck hat eine Option, genau das zu tun.

    $ perl -e' 
        use YAML::Syck qw(Dump); 
        local $YAML::Syck::SingleQuote = 1; 
        print(Dump({abc=>"1:16"})); 
    ' 
    --- 
    "abc": '1:16' 
    

    (Sie wissen nicht, wie ich diese Option früher verpasst!)

    +0

    Ich habe versucht YAML :: XS. Es hat das gleiche Problem. Der YAML :: Syck macht mich sehr nervös. Hier ist ein Zitat von CPAN Seite für YAML :: Syck - "Dieses Modul hat eine Menge bekannter Probleme und wurde nur seit 2007 semi-aktiv gepflegt. Wenn Sie ein Problem mit ihm finden, wird wahrscheinlich nicht behoben, es sei denn, Sie bieten einen Patch in Git, der fertig ist. " Die Ruby-Dokumentation sagt im Grunde das Gleiche. –

    +0

    Ich habe nicht gesagt, dass Sie YAML :: XS verwenden sollten - Eigentlich sind Sie schon nach dem, was Sie gesagt haben - ich sagte, Sie sollten versuchen, libyaml oder libsyck \ in Ruby zu verwenden. – ikegami