to_json и to_xml дают разные результаты в рельсах

Начинающий программист рельсов здесь. Я пытаюсь преобразовать хэш как в xml, так и в json, но результат отличается.

Вот хэш:

{:exchangeRates=>[{:baseCurrency=>"USD", :quoteCurrency=>"EUR", :amount=>1, :nprices=>1, :conversions=>[{:date=>Tue, 20 Nov 2012 21:00:00 +0000, :ask=>"0.7813", :bid=>"0.7813"}]}, {:baseCurrency=>"CAD", :quoteCurrency=>"EUR", :amount=>1, :nprices=>1, :conversions=>[{:date=>Tue, 20 Nov 2012 21:00:00 +0000, :ask=>"0.7839", :bid=>"0.7837"}]}]}

Вот соответствующий код рендеринга

format.json { render :json => { :response => rates.to_hash() } }

и вот JSON (это то, что я хочу)

{"response": {"exchangeRates": [
    {
        "baseCurrency": "USD",
        "quoteCurrency": "EUR",
        "amount": 1,
        "nprices": 1,
        "conversions": [{
            "date": "2012-11-20T21:00:00+00:00",
            "ask": "0.7813",
            "bid": "0.7813"
        }]
    },
    {
        "baseCurrency": "CAD",
        "quoteCurrency": "EUR",
        "amount": 1,
        "nprices": 1,
        "conversions": [{
            "date": "2012-11-20T21:00:00+00:00",
            "ask": "0.7839",
            "bid": "0.7837"
        }]
    }
]}}

Вот мой код рендеринга xml:

format.xml { render :xml => rates.to_hash(), :root => 'response' }

Вот вывод xml (есть дополнительные теги, где я помещаю массивы):

<response>
    <exchangeRates type="array">
        <exchangeRate>
            <baseCurrency>USD</baseCurrency>
            <quoteCurrency>EUR</quoteCurrency>
            <amount type="integer">1</amount>
            <nprices type="integer">1</nprices>
            <conversions type="array">
                <conversion>
                    <date type="datetime">2012-11-20T21:00:00+00:00</date>
                    <ask>0.7813</ask>
                    <bid>0.7813</bid>
                </conversion>
            </conversions>
        </exchangeRate>
        <exchangeRate>
            <baseCurrency>CAD</baseCurrency>
            <quoteCurrency>EUR</quoteCurrency>
            <amount type="integer">1</amount>
            <nprices type="integer">1</nprices>
            <conversions type="array">
                <conversion>
                    <date type="datetime">2012-11-20T21:00:00+00:00</date>
                    <ask>0.7839</ask>
                    <bid>0.7837</bid>
                </conversion>
            </conversions>
        </exchangeRate>
    </exchangeRates>
</response>

Как видите, он добавляет дополнительные теги атрибутов «массива», то есть exchangeRates и конверсии. Как мне заставить его форматировать так же, как json? Мне также не нужны атрибуты ни в одном из тегов. Я знаю, что вы можете передавать атрибуты, такие как :root => 'response', но после некоторого поиска я не могу найти список этих атрибутов в Интернете.

Любая помощь будет принята с благодарностью, спасибо!


person willard    schedule 22.11.2012    source источник


Ответы (1)


Это тот случай, когда лучше сразу перейти к исходному коду. Метод to_xml находится в модуле ActiveModel::Serializer, вот встроенные документы, в которых ничего не говорится о тегах атрибутов type="array". Копните немного глубже, и вы увидите, что они появляются в одном и том же файле в строке 130 метода с именем add_associations.

rails/activemodel/lib/active_model/serializers/xml.rb:130

type = options[:skip_types] ? { } : {:type => "array"}

Это говорит нам о том, что есть опция skip_types, которая, похоже, нигде не задокументирована. Попробуйте передать это to_xml, и вы получите желаемое поведение:

a = [1, 2, 3]
a.to_xml
#=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<fixnums type=\"array\">\n  <fixnum type=\"integer\">1</fixnum>\n  <fixnum type=\"integer\">2</fixnum>\n  <fixnum type=\"integer\">3</fixnum>\n</fixnums>\n"
a.to_xml(:skip_types => true)
#=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<fixnums>\n  <fixnum>1</fixnum>\n  <fixnum>2</fixnum>\n  <fixnum>3</fixnum>\n</fixnums>\n"

Вы заметите, что все добавленные атрибуты type исчезли.

Так что просто передайте ту же опцию render и вы получите желаемый результат:

format.xml { render :xml => rates.to_hash(), :root => 'response', :skip_types => true }
person Chris Salzberg    schedule 22.11.2012
comment
Спасибо за ответ. Дело в том, что я хочу ПОЛНОСТЬЮ избавиться от этого тега массива, то есть для вашего примера мой желаемый xml будет: <?xml version=1.0?><fixnum>1</fixnum><fixnum>2</fixnum><fixnum>3</fixnum>. После просмотра кода я не думаю, что это возможно. Возможно, я могу вернуться к дизайнеру и попросить его изменить вывод, чтобы включить этот тег-оболочку, поскольку это кажется правильным способом создания xml. Как вы думаете? - person willard; 23.11.2012
comment
Понимаю. Но это не совсем то, что вы спросили. Вы сказали, что хотите, чтобы xml был похож на JSON, а в JSON есть ключ с именем "exchangeRates", за которым следует массив. Если вы полностью удалите тег, то (как я это вижу) вы создаете xml, который отличается от JSON. - person Chris Salzberg; 24.11.2012