2012-04-12 61 views
0

我正在从Android设备向Rails应用程序发送XML结构。这个XML的结构看起来像这样:如何解析通过POST发送到Ruby on Rails的XML?

<Updates> 
<Update type="1"><Data>...</Data><Something>...</Something></Update> 
<Update type="2"><Data>...</Data><Something>...</Something></Update> 
<Update type="3"><Data>...</Data><Something>...</Something></Update> 
</Updates> 

我看起来好像Rails把它变成了一个散列。

我怎样才能通过每个标签(这个数字变化),看看它的类型属性,然后使用其他元素内的数据?

我一直在为此而苦苦挣扎。

这里是我不断收到(类型错误(不能转换成字符串整数))的错误:

Started POST "/update/index?auth_token=rAyrJgr4w6LNBSXRoYie" for 127.0.0.1 at 2012-04-11 23:44:29 -0500 
Processing by UpdateController#index as XML 
    Parameters: {"Updates"=>{"Update"=>{"type"=>"2", "levelID"=>"7", "S1"=>"1", "S2"=>"0", "S3"=>"0"}}, "auth_token"=>"rAyrJgr4w6LNBSXRoYie"} 
WARNING: Can't verify CSRF token authenticity 
    User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."authentication_token" = 'rAyrJgr4w6LNBSXRoYie' LIMIT 1 
    (0.1ms) begin transaction 
    (0.5ms) UPDATE "users" SET "current_sign_in_at" = '2012-04-12 04:44:29.385406', "sign_in_count" = 2, "updated_at" = '2012-04-12 04:44:29.386311' WHERE "users"."id" = 1 
    (2.2ms) commit transaction 
Completed 500 Internal Server Error in 9ms 

TypeError (can't convert String into Integer): 
    app/controllers/update_controller.rb:21:in `fetch' 
    app/controllers/update_controller.rb:21:in `block in index' 
    app/controllers/update_controller.rb:19:in `each' 
    app/controllers/update_controller.rb:19:in `index' 


    Rendered /Users/colegroff/.rvm/gems/[email protected]/gems/actionpack-3.2.1/lib/action_dispatch/middleware/templates/rescues/_trace.erb (50.2ms) 
    Rendered /Users/colegroff/.rvm/gems/[email protected]/gems/actionpack-3.2.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.8ms) 
    Rendered /Users/colegroff/.rvm/gems/[email protected]/gems/actionpack-3.2.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (55.6ms) 
---------------------------------------------------- 
params[:Updates].class: ActiveSupport::HashWithIndifferentAccess 
---------------------------------------------------- 
update.inspect: ["type", "2"] 

这里是我的控制器:

class UpdateController < ApplicationController 

    before_filter :authenticate_user! 

    def index 

    output = "" 

    puts "----------------------------------------------------" 
    puts "params[:Updates].class: #{params[:Updates].class.to_s}" 
    puts "----------------------------------------------------" 

    if !params[:Updates].nil? 
     params[:Updates][:Update].each do |update| 
     puts "update.inspect: #{update.inspect}" 
     puts "update.fetch(\"type\"): #{update.fetch("type")}" 
     puts "update.fetch(\"type\").class.to_s: #{update.fetch("type").class.to_s}" 
     update_type = update.fetch("type") 
     puts "update_type: #{update_type}" 

     case update_type 

     when "0" 
      puts "inside case 0" 
      number_of_bug_bucks = update.fetch("numberOfBugBucks") 
      update_bug_bucks(number_of_bug_bucks) 
      output += "<Update type='0'>" 
      output += "<numberOfBugBucks>#{current_user.bug_bucks.to_s}</numberOfBugBucks>" 
      output += "</Update>" 
     when "2" 
      puts "inside case 2" 
      level_id = update.fetch("levelID") 
      star_1 = update.fetch("S1") 
      star_2 = update.fetch("S2") 
      star_3 = update.fetch("S3") 
      updated_level = update_level_data(level_id, star_1, star_2, star_3) 
      output += "<Update type='2'>" 
      output += "<levelID>#{updated_level[0].level_id}</levelID>" 
      output += "<S1>#{updated_level[0].star_1}</S1>" 
      output += "<S2>#{updated_level[0].star_2}</S2>" 
      output += "<S3>#{updated_level[0].star_3}</S3>" 
      output += "</Update>" 
     when "3" 
      puts "inside case 3" 
      tower_id = update.fetch("towerID") 
      tower_state = update.fetch("towerState") 
      updated_tower = update_tower_data(tower_id, tower_state) 
      output += "<Update type='3'>" 
      output += "<towerID>#{updated_tower[0].tower_id}</towerID>" 
      output += "<towerState>#{updated_tower[0].tower_state}</towerState>" 
      output += "</Update>" 
     when "4" 
      puts "inside case 4" 
      authentication_token = update.fetch("AuthToken") 
      update_is_logged_in = update_is_logged_in(authentication_token) 
      output += "<Update type='4'>" 
      output += "<AuthToken>#{update_is_logged_in[0].authentication_token}</AuthToken>" 
      output += "</Update>" 
     end 

     puts "----------------------------------------------------" 

     end 
    end 

    puts "output: #{output}" 
    puts "----------------------------------------------------" 

    respond_to do |format| 
     format.xml { render :xml => "<Updates>" + output + "</Updates>" } 
    end 

    end 

    private # private methods below: 

    def update_bug_bucks(number_of_bug_bucks) 
     current_user.update_attribute("bug_bucks", number_of_bug_bucks) 
    end 

    def update_level_data(level_id, star_1, star_2, star_3)  
     updated_level = Level.where("level_id = ? AND user_id = ?", level_id, current_user.id)  
     if updated_level.empty? 
     current_user.levels.create(attributes = {:level_id => level_id, :star_1 => star_1, :star_2 => star_2, :star_3 => star_3}) 
     else 
     updated_level[0].update_attributes(:star_1 => star_1, :star_2 => star_2, :star_3 => star_2) 
     end  
     updated_level  
    end 

    def update_tower_data(tower_id, tower_state)  
     updated_tower = Tower.where("tower_id = ? AND user_id = ?", tower_id, current_user.id)  
     if updated_tower.empty? 
     current_user.towers.create(attributes = {:tower_id => tower_id, :tower_state => tower_state}) 
     else 
     updated_tower[0].update_attributes(:tower_state => tower_state) 
     end  
     updated_tower  
    end 

    def update_is_logged_in(authentication_token)  
     logged_out_user = User.where("authentication_token = ?", authentication_token).first 
     logged_out_user.reset_authentication_token! 
     logged_out_user.update_attribute("is_logged_in", false)  
    end 
    ############################################################################################################################ 
    ############################################################################################################################ 

end 

回答

1

你有没有尝试过类似

params[:updates][:update].each do |update| 
    puts "do something with this update: #{update.inspect}" 
end 

如果rails为您提供散列,只需遍历您想要的散列中的集合即可。处理POST的控制器看起来像什么?

+0

我已经完成了您的建议。我还编辑了我的原始帖子,以显示我收到的错误和我的控制器。为什么“TypeError(无法将字符串转换为整数)”继续发生? – groffcole 2012-04-12 04:50:29

+0

似乎有时在循环中的“更新”有时是一个HashWithIndifferentAccess,有时它是一个数组。什么可能是解决方案? – groffcole 2012-04-12 05:59:16

+0

如果它是一个数组,索引*必须是整数(所以'update.fetch(“type”)'失败,因为update是一个数组)。基于这个XML,我认为Rails应该有一个params [:Updates],它是一个'HashWithIndifferentAccess',它包含一个'HashWithIndifferentAccess'更新,它们本身就是'Arrays'。因此,不是找回可以调用'.fetch'的Update对象,而是获得一个'Array',它在索引0处具有“type”,在索引1处具有“n”。您的代码SO接近工作,但它只是不正确地处理更新对象。 – tjarratt 2012-04-13 02:33:55

3

这可能是有点晚,但任何人谁是指在本文的后面,这是我如何解决类似的情况

string = request.body.read # rather than using params you can get get/post message with this 

然后使用Nokogiri如下

doc = Nokogiri::XML(string) 
doc.xpath("//Upadate") 

更多信息如何使用Nokogiri请参考http://nokogiri.org/tutorials

+1

为了简单起见,我使用'Hash.from_xml'解析xml字符串(Rails v4.2.1)。这里是这个函数的[reference](http://apidock.com/rails/Hash/from_xml/class)。 – Yang 2016-02-23 23:12:57