Search code examples
ruby-on-railsrubyxmljsonsavon

Wrong Savon interpretation of my Ruby code


I've got a bad interpretation of my code or I don't understand how let it works.

Basically I've got this request:

def create_request
  return {} unless [email protected]?

  req = {
    ProfileIDVersion: {
      ID: ENV['GBGROUP_PROFILE_ID'],
      Version: ENV['GBGROUP_PROFILE_VERSION']
    },
    CustomerReference: reference,
    InputData: {
      Personal: {
        PersonalDetails: {
          Title: @person.title,
          Forename: @person.first_name,
          MiddleName: @person.middle_name,
          Surname: @person.last_name,
          Gender: @person.gender,
          DOBDay: @person.dob.nil? ? '' : @person.dob.day,
          DOBMonth: @person.dob.nil? ? '' : @person.dob.month,
          DOBYear: @person.dob.nil? ? '' : @person.dob.year
        }
      },
      Addresses: []
    }
  }
  address_count = 0
  @person.addresses.each do |address|
    Rails.logger.debug 'Adding address'
    if address_count == 0
      Rails.logger.debug 'Adding current address'
      req[:InputData][:Addresses] << {
        CurrentAddress: {
          Country: address.country,
          ZipPostcode: address.postcode,
          AddressLine1: address.line1,
          AddressLine2: address.line2,
          AddressLine3: address.town_city,
          FirstYearOfResidence: address.from_year,
          LastYearOfResidence: address.to_year
        }
      }
    else
      Rails.logger.debug 'Adding previous address'
      req[:InputData][:Addresses] << {
        "PreviousAddress#{address_count}" => {
          Country: address.country,
          ZipPostcode: address.postcode,
          AddressLine1: address.line1,
          AddressLine2: address.line2,
          AddressLine3: address.town_city,
          FirstYearOfResidence: address.from_year,
          LastYearOfResidence: address.to_year
        }
      }
    end
    address_count += 1
  end
end

where I'm expecting to have in output:

<ns:Addresses>
<ns:CurrentAddress>
<ns:Country>United Kingdom</ns:Country>
<ns:ZipPostcode>SP9 7EW</ns:ZipPostcode>
<ns:AddressLine1>3 Bayeux Mews</ns:AddressLine1>
<ns:AddressLine2>St Georges Road</ns:AddressLine2>
<ns:AddressLine3>Tidworth</ns:AddressLine3>
<ns:FirstYearOfResidence>2015</ns:FirstYearOfResidence>
<ns:LastYearOfResidence>2015</ns:LastYearOfResidence>
</ns:CurrentAddress>
<ns:PreviousAddress1>
<ns:Country>United Kingdom</ns:Country>
<ns:ZipPostcode>SP9 7EW</ns:ZipPostcode>
<ns:AddressLine1>3 Bayeux Mews</ns:AddressLine1>
<ns:AddressLine2>St Georges Road</ns:AddressLine2>
<ns:AddressLine3>Tidworth</ns:AddressLine3>
<ns:FirstYearOfResidence>2012</ns:FirstYearOfResidence>
<ns:LastYearOfResidence>2015</ns:LastYearOfResidence>
</ns:PreviousAddress1>
</ns:Addresses>

instead I've got that:

<ns:Addresses>
<ns:CurrentAddress>
<ns:Country>United Kingdom</ns:Country>
<ns:ZipPostcode>SP9 7EW</ns:ZipPostcode>
<ns:AddressLine1>3 Bayeux Mews</ns:AddressLine1>
<ns:AddressLine2>St Georges Road</ns:AddressLine2>
<ns:AddressLine3>Tidworth</ns:AddressLine3>
<ns:FirstYearOfResidence>2015</ns:FirstYearOfResidence>
<ns:LastYearOfResidence>2015</ns:LastYearOfResidence>
</ns:CurrentAddress>
</ns:Addresses>
<ns:Addresses>
<ns:PreviousAddress1>
<ns:Country>United Kingdom</ns:Country>
<ns:ZipPostcode>SP9 7EW</ns:ZipPostcode>
<ns:AddressLine1>3 Bayeux Mews</ns:AddressLine1>
<ns:AddressLine2>St Georges Road</ns:AddressLine2>
<ns:AddressLine3>Tidworth</ns:AddressLine3>
<ns:FirstYearOfResidence>2012</ns:FirstYearOfResidence>
<ns:LastYearOfResidence>2015</ns:LastYearOfResidence>
</ns:PreviousAddress1>
</ns:Addresses>

As you can see I've got two Addresses block, one with current address and the other with previous address, but need just one address block.

The guess the problem might be calling multiple time:

req[:InputData][:Addresses] <<

but I don't actually know how to solve it in order to have all the addresses in one block.

This is what I've got from my Spec test:

    "Addresses": [
      {
        "CurrentAddress": {
          "Country": "United Kingdom",
          "ZipPostcode": "OX4 2QX",
          "AddressLine1": "36 Brasenose Driftway",
          "AddressLine2": "Oxford",
          "AddressLine3": null,
          "FirstYearOfResidence": 2002,
          "LastYearOfResidence": 2014
        }
      },
      {
        "PreviousAddress1": {
          "Country": "United Kingdom",
          "ZipPostcode": "AB1 1AA",
          "AddressLine1": "21 A Street",
          "AddressLine2": "Aberdeen",
          "AddressLine3": null,
          "FirstYearOfResidence": 1998,
          "LastYearOfResidence": 2002
        }
      }
    ]

Thanks to anyone will spend a minute to reply, Caterpillar.


Solution

  • The bit of savon that does the conversion between a ruby hash and xml is a gem called gyoku. For gyoku, an array value results in multiple elements (whose name is given by the key). If you don't want that, then the value for the Addresses key must be a hash i.e your input should look like

    {"Addresses" => {
        "CurrentAddress" => {
           "Country" => "United Kingdom",
           ...
        },
        "Previous" => {
           "Country" => "United Kingdom",
           ...
        }
    }