【问题标题】:Extracting hash key that may or may not be an array提取可能是也可能不是数组的哈希键
【发布时间】:2019-12-03 21:25:22
【问题描述】:

我正在进行一个返回 XML(JSON 也可用)的 API 调用,并且响应正文将显示错误(如果有)。可能只有一个错误或多个错误。当 XML(或 JSON)被解析为散列时,当存在多个错误时,保存错误的键将是一个数组,但当仅存在一个错误时,它将只是一个标准键。这使得解析变得困难,因为我似乎无法想出适合这两种情况的一行代码

当出现一个错误时,对 API 的调用会返回此内容

<?xml version=\"1.0\" encoding=\"utf-8\"?><response><version>1.0</version><code>6</code><message>Data validation failed</message><errors><error><parameter>rptFilterValue1</parameter><message>Parameter is too small</message></error></errors></response>

当出现多个错误时

<?xml version=\"1.0\" encoding=\"utf-8\"?><response><version>1.0</version><code>6</code><message>Data validation failed</message><errors><error><parameter>rptFilterValue1</parameter><message>Parameter is too small</message></error><error><parameter>rptFilterValue2</parameter><message>Missing required parameter</message></error></errors></response>

我使用以下将 XML 转换为哈希

Hash.from_xml(response.body).deep_symbolize_keys

这将返回以下哈希。

只有一个错误时,哈希是这样的

{:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}}}}

当有 2 个错误时,哈希是这样的

{:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>[{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}, {:parameter=>"rptFilterValue2", :message=>"Missing required parameter"}]}}}

当我第一次测试 API 响应时,我遇到了多个错误,所以我获取错误消息的方式是这样的

data = Hash.from_xml(response.body).deep_symbolize_keys
if data[:response].has_key?(:errors)
  errors = data[:response][:errors][:error].map{|x| "#{x.values[0]} #{x.values[1]}"}

但是,当只有一个错误时,代码会因参数未定义的方法“值”而出错

我发现的唯一实际解决方法是测试错误键的类。当 Array 我使用一种方法进行提取时,当 Hash 我使用另一种方法时。

if data[:response][:errors][:error].class == Array
  errors = data[:response][:errors][:error].map{|x| "#{x.values[0]} #{x.values[1]}"}
else
  errors = data[:response][:errors][:error].map{|x| "#{x[1]}"}
end

但我只是讨厌讨厌讨厌它。必须有一种方法可以从可能是也可能不是数组的键中提取 xml/json 数据。解决方案可能是在从 xml 到哈希的转换中,而不是在解析实际哈希时。我在网上找不到任何东西。

我将不胜感激任何帮助或提示。

【问题讨论】:

  • 旁注:使用Object#is_a?代替直接类比较;后者对于所有派生类都将失败。 data[:response][:errors][:error].is_a?(Array)(或者你可以比较不等式的类:data[:response][:errors][:error].class &lt;= Array

标签: ruby-on-rails json ruby hash


【解决方案1】:

如果您使用的是 Rails,如果您可以先使用 .dig,则可以使用 Array#wrap

single = {:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}}}}

Array.wrap(single.dig(:response, :errors, :error))

这会返回一个大小为 1 的数组:

[
  {
      :message => "Parameter is too small",
    :parameter => "rptFilterValue1"
  }
]

对于倍数:

multiple = {:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>[{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}, {:parameter=>"rptFilterValue2", :message=>"Missing required parameter"}]}}}

Array.wrap(multiple.dig(:response, :errors, :error))

这会返回一个大小为 2 的数组:

[
  {
      :message => "Parameter is too small",
    :parameter => "rptFilterValue1"
  },
  {
      :message => "Missing required parameter",
    :parameter => "rptFilterValue2"
  }
]

【讨论】:

  • 谢谢杰。所以 wrap 方法使键成为一个数组...除非它已经是一个数组,才有意义!
  • 是的。它基本上做了你所做的,但解决了一些边缘情况并将其包装在公共类方法中。它的代码很短:github.com/rails/rails/blob/…
【解决方案2】:

您可以使用Nokogirixpath 解析XML,即使选择器指向单个元素,它也会返回数组

errors = Nokogiri::XML(xml_response).xpath('//error')
errors.map { |e| e.children.each_with_object({}) { |x, h| h[x.name] = x.content } }

您的 API 响应与单个错误给出了

=> [{"parameter"=>"rptFilterValue1", "message"=>"Parameter is too small"}]

API 结果出现多个错误

=> [{"parameter"=>"rptFilterValue1", "message"=>"Parameter is too small"}, {"parameter"=>"rptFilterValue2", "message"=>"Missing required parameter"}]

如果没有 error 元素,您将得到一个空数组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 2011-11-26
    • 2016-02-27
    • 2016-09-17
    相关资源
    最近更新 更多