Mam ciąg, który wygląda jak hash:
"{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }"
Jak uzyskać z tego hash? lubić:
{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }
Ciąg może mieć dowolną głębokość zagnieżdżenia. Ma wszystkie właściwości wpisywania prawidłowego skrótu w Rubim.
Odpowiedzi:
Ciąg utworzony przez wywołanie
Hash#inspect
można przekształcić z powrotem w hash, wywołująceval
go. Jednak wymaga to tego samego dla wszystkich obiektów w skrócie.Jeśli zacznę od skrótu
{:a => Object.new}
, to jego reprezentacja w postaci ciągu to"{:a=>#<Object:0x7f66b65cf4d0>}"
i nie mogę użyć go,eval
aby zmienić go z powrotem w hash, ponieważ#<Object:0x7f66b65cf4d0>
nie jest poprawna składnia Ruby.Jeśli jednak wszystko, co znajduje się w haszu, to łańcuchy, symbole, liczby i tablice, powinno to działać, ponieważ mają one reprezentacje ciągów, które są poprawną składnią Rubiego.
źródło
eval
jako hash, upewniając się, że powyższa instrukcja jest ważna dla tego ciągu.eval
w złym miejscu to ogromna luka w zabezpieczeniach. Wszystko, co znajduje się w ciągu, zostanie ocenione. Wyobraź sobie więc, że ktoś wstrzyknął w APIrm -fr
Dla innego ciągu możesz to zrobić bez użycia niebezpiecznej
eval
metody:źródło
JSON.parse(hash_as_string.gsub("=>", ":").gsub(":nil,", ":null,"))
Byłaby to szybka i brudna metoda
Ale ma to poważne konsekwencje dla bezpieczeństwa.
Wykonuje wszystko, co zostanie przekazane, musisz mieć 110% pewności (tak jak w przypadku, gdy nigdzie nie ma żadnych danych wejściowych użytkownika), że zawierałyby tylko poprawnie uformowane skróty lub nieoczekiwane błędy / straszne stworzenia z kosmosu mogą zacząć pojawiać się.
źródło
Może YAML.load?
źródło
Ten krótki fragment wystarczy, ale nie widzę, żeby działał z zagnieżdżonym hashem. Myślę, że to całkiem urocze
Kroki 1. Eliminuję „{”, „}” i „:” 2. Dzielę łańcuch wszędzie tam, gdzie znajdzie „,” 3. Dzielę każdy z podciągów, które zostały utworzone za pomocą podziału, gdy tylko znajdzie a '=>'. Następnie tworzę hash z dwiema stronami haszyszu, które właśnie rozdzieliłem. 4. Zostaje mi tablica skrótów, które następnie łączę.
PRZYKŁADOWE WPROWADZENIE: "{: user_id => 11,: blog_id => 2,: comment_id => 1}" WYNIKI WYNIKÓW: {"user_id" => "11", "blog_id" => "2", "comment_id" = > „1”}
źródło
{}:
znaków z wartości wewnątrz ciągłego skrótu?Dotychczasowe rozwiązania obejmują niektóre przypadki, ale niektóre pomijają (patrz poniżej). Oto moja próba dokładniejszej (bezpieczniejszej) konwersji. Znam jeden przypadek narożny, którego to rozwiązanie nie obsługuje, czyli symbole jednoznakowe złożone z dziwnych, ale dozwolonych znaków. Na przykład
{:> => :<}
jest to poprawny hash ruby.Umieściłem ten kod również na githubie . Ten kod zaczyna się od ciągu testowego, aby wykonać wszystkie konwersje
Oto kilka uwag na temat innych rozwiązań tutaj
eval
co jest dla mnie zbyt przerażające (jak słusznie zauważa Toms).źródło
:nil
aby:null
do uchwytu danym tajemniczości.Miałem ten sam problem. Przechowywałem haszysz w Redis. Podczas pobierania tego skrótu był to ciąg. Nie chciałem dzwonić
eval(str)
ze względów bezpieczeństwa. Moim rozwiązaniem było zapisanie skrótu jako łańcucha json zamiast ruby. Jeśli masz taką opcję, używanie json jest łatwiejsze.TL; DR: użyj
to_json
iJSON.parse
źródło
to_json
iJSON.parse
Wolę nadużywać ActiveSupport :: JSON. Ich podejście polega na zamianie haszyszu na yaml, a następnie załadowaniu go. Niestety konwersja do yaml nie jest prosta i prawdopodobnie chciałbyś pożyczyć ją od AS, jeśli nie masz jeszcze AS w swoim projekcie.
Musimy także przekonwertować wszystkie symbole na zwykłe klucze łańcuchowe, ponieważ symbole nie są odpowiednie w JSON.
Jednak nie jest w stanie obsłużyć skrótów, które zawierają ciąg daty (nasze ciągi daty nie są otoczone ciągami, co jest przyczyną dużego problemu):
string = '{' last_request_at ': 2011-12-28 23:00:00 UTC}'
ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))
Spowoduje to błąd nieprawidłowego ciągu JSON podczas próby przeanalizowania wartości daty.
Chciałbym mieć jakiekolwiek sugestie, jak sobie z tym poradzić
źródło
ActiveSupport::JSON.decode(response.body, symbolize_keys: true)
działa w railsach 4.1 i obsługuje symbole bez cudzysłowów {: a => 'b'}
po prostu dodaj to do folderu inicjalizatorów:
źródło
Zbudowałem gem hash_parser, który najpierw sprawdza, czy hash jest bezpieczny, czy nie używa
ruby_parser
gem. Dopiero wtedy stosujeeval
.Możesz go używać jako
Testy na https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb dają więcej przykładów rzeczy, które przetestowałem, aby upewnić się, że eval jest bezpieczny.
źródło
Prosimy o rozważenie tego rozwiązania. Biblioteka + specyfikacja:
Plik
lib/ext/hash/from_string.rb
::Plik
spec/lib/ext/hash/from_string_spec.rb
::źródło
it "generally works"
ale niekoniecznie? Byłbym bardziej rozwlekły w tych testach.it "converts strings to object" { expect('...').to eql ... }
it "supports nested objects" { expect('...').to eql ... }
Doszedłem do tego pytania po napisaniu w tym celu jednej linijki, więc udostępniam swój kod na wypadek, gdyby to komuś pomogło. Działa dla łańcucha z tylko jedną głębokością poziomu i możliwymi wartościami pustymi (ale nie zerowymi), na przykład:
Kod to:
źródło
Natrafiłem na podobny problem, który wymagał użycia funkcji eval ().
W mojej sytuacji pobierałem pewne dane z API i zapisywałem je lokalnie do pliku. Następnie można pobrać dane z pliku i użyć skrótu.
Użyłem IO.read () do wczytania zawartości pliku do zmiennej. W tym przypadku IO.read () tworzy go jako ciąg.
Następnie użył eval () do konwersji ciągu znaków na Hash.
Wystarczy wspomnieć, że IO jest przodkiem File. Możesz więc zamiast tego użyć File.read, jeśli chcesz.
źródło