Question

I want to deep copy a json object in Ruby. However when I call clone the json object it doesn't seem to do a deep copy. Is it possible to or am I doing something wrong. Here is the relevant snippet of code of what I am doing now:

idFile = File.new(options[:idFile])
idFile.each_line do |id|
    jsonObj = getJson(id)
    copyObj = jsonObj.clone
    copyObj['details']['payload'] = Base64.decode64(copyObj['payload'])
    copyObj['key'] = 1
    jsonObj['details']['payload'] = Base64.decode64(jsonObj['payload'])
    jsonObj['key'] = 2
    send(copyObj)
    send(jsonObj)  #error here
end

def getJson(id)
    idData = getData(id)
    idJson = JSON.parse!(idData)
    idJson = idJson['request'][0]
    return idJson
end

The error for me occurs because of the decode calls. The first decode call already decodes the object, and the second one tries to decode the same data again, which errors out in the second send call because at that point the data is gibberish.
How do I deep copy that json object?

Was it helpful?

Solution

JSON is merely text - and in this case it is assumed that the object can round-trip through JSON serialization.

Thus the simplest approach to go Object->JSON(Text)->Object to obtain a true deep clone; alternatively, deserialize the JSON twice (once for the deep clone, as the two deserializations produce in dependent object graphs). Note that Object here is not JSON, but merely the deserialized representation (e.g. Hashes and Arrays) of the data as a standard Ruby objects.

# Standard "deep clone" idiom using an intermediate serialization.
# This is using JSON here but it is the same with other techniques that walk
# the object graph (such as Marshal) and generate an intermediate serialization
# that can be restored later.
jsonObj = getJson(id)
jsonObj["foo"] = "change something"
json = JSON.generate(jsonObj)
copyObj = JSON.parse(json)

# Or, assuming simple deep clone of original, just deserialize twice.
# (Although in real code you'd only want to get the JSON text once.)
jsonObj = getJson(id)
copyObj = getJson(id)

As noted, clone does not do this serialization/deserialization step, but merely ascribes to shallow Object#clone semantics (actually, there is no Hash#clone, so it uses Object#clone's implementation directly):

Produces a shallow copy of obj—the instance variables of obj are copied, but not the objects they reference ..

OTHER TIPS

If you are just looking to do a deep copy of any arbitrary Ruby object, try deep_dive.

https://rubygems.org/gems/deep_dive

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top