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?
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 ..
If you are just looking to do a deep copy of any arbitrary Ruby object, try deep_dive.
https://rubygems.org/gems/deep_dive
Related
I need to get some values for a hash but, I don't know how can I do it!
My data:
data = {
name: "Pedro Álvares",
age: 35,
sensitive_data: {
cpf_cnpj=>27046645678,
fantasy_name: "I have the power"
}
}
I search for ruby methods and find the method values_at, but this method gets only the first_data like name, age.
If I use:
name = data.values_at(:name)
the field returned is Pedro Álvares
but if I try use:
fantasy_name = data.values_at(:fantasy_name)
the data returned is nil.
How can I get the field without using data[:sensitive_data][:fantasy_name]?
Because you know the correct structure of the hash you should write:
data[:sensitive_data][:fantasy_name]
#=> "I have the power"
You should not use dig here. Why? Suppose you accidently wrote
data.dig(:sesnitive_data, :fantasy_name)
The would return nil (because data has no key :sesnitive). Depending on the context the error might not surface until sometime later, making debugging more difficult than is necessary.
By contrast, if you wrote
data[:sesnitive_data][:fantasy_name]
data[:sesnitive_data] would return nil (because data has no key :sesnitive_data) and then nil[:sesnitive_data] would raise an exception, informing you that nil has no method []. That is precisely what you want to happen: you want to be notified of the error immediately, and have the reason for it it pinpointed, so you can easily correct your code.
Hash#dig, Array#dig and Struct#dig (which call each other) have their uses (when you do not know the structures of objects in advance--a hash's keys, for example), but those methods should not be used when an object's structure is known.
You could get the nested value by dig method:
data.dig(:sensitive_data, :fantasy_name) # => "I have the power"
I'm trying to understand if it's possible to take a serialized protobuf that makes up part of another protobuf and merge them together without having to deserialize the first protobuf.
For example, given a protobuf wrapper:
syntax = "proto2";
import "content.proto";
message WrapperContent {
required string metatData = 1;
required Content content = 2;
}
And then imagine we get a serialized version of Content below (i.e. Content is coming that is coming from a remote client):
syntax = "proto2";
message Content {
required string name = 1;
required bytes payload = 2;
}
Do you know if any way I can inject the serialized Content into the WrapperContent without first having to deserialize Content.
The reason I'm trying to inject Content without deserializing it, is that I'm try and save on the overhead of deserializing the message.
If that answer is, no, it's not possible. That is still helpful.
Thanks, Mike.
In protobuf, submessages are stored like bytes fields.
So you can make a modified copy of your wrapper:
message WrapperContentBytes {
required string metatData = 1;
required bytes content = 2;
}
and write the already serialized content data into the content field.
Decoders can use the unmodified WrapperContent message to decode also the submessage. The binary data on the wire will be the same so decoders do not know the difference.
I need to dump a hash object to JSON and I was wondering which of these three, to_json, JSON.generate or JSON.dump, is the preferred way to do it.
I've tested the results of these methods and they are the same:
> {a: 1, b: 2}.to_json
=> "{\"a\":1,\"b\":2}"
> JSON.generate({a: 1, b: 2})
=> "{\"a\":1,\"b\":2}"
> JSON.dump({a: 1, b: 2})
=> "{\"a\":1,\"b\":2}"
From docs:
JSON.generate only allows objects or arrays to be converted to JSON syntax. to_json, however, accepts many Ruby classes even though it acts only as a method for serialization
and
[JSON.dumps] is part of the implementation of the load/dump interface of Marshal and YAML.
If anIO (an IO-like object or an object that responds to the write method) was given, the resulting JSON is written to it.
JSON.generate only allows objects or arrays to be converted to JSON syntax.
to_json accepts many Ruby classes even though it acts only as a method for serialization
JSON.generate(1)
JSON::GeneratorError: only generation of JSON objects or arrays allowed
1.to_json
=> "1"
JSON.dump: Dumps obj as a JSON string, calls generate on the object and returns the result.
You can get more info from here
For dumping arrays, hashs and objects (converted by to_hash), these 3 ways are equivalent.
But JSON.generate or JSON.dump only allowed arrays, hashs and objects.
to_json accepts many Ruby classes even though it acts only as a method for serialization, like a integer:
JSON.generate 1 # would be allowed
1.to_json # => "1"
JSON.generate took more options for output style (like space, indent)
And JSON.dump, output default style, but took a IO-like object as second argument to write, third argument as limit number of nested arrays or objects.
Working with a huge hash (300MB), to_json was unable to generate the json string, it crashed.
However JSON.generate did work, and so did JSON.dump.
I'm using Gson library but when it serializes the JsonArray object, it seems to serialize this as an object rather than a JSON array. i.e.
{ elements: [ {name:"value1}, {name:"value2"}]}
How do I remove the elements from being serialized?
I went to see the doctor, because my foot hurt when I walked on it. The doctor said, "Don't walk on it."
Generally, when working with an API like Gson, one would rather not even know that JsonArray exists, and they'd instead just use the data binding part of the API. So, instead of manually building a JsonArray, and then deal with serializing it, just feed a Java List or array to Gson.toJson(). For example:
List list = new ArrayList();
list.add("one");
list.add(2);
list.add(new Foo());
Gson gson = new Gson();
String json = gson.toJson(list);
System.out.println(json);
If that approach doesn't fit your needs and you're stuck using a JsonArray for some reason, then you might be tempted to just call its toString() method, since that does currently create what's ultimately desired, I wouldn't use it, because there is nothing in the documentation that says the toString() is guaranteed to create a valid JSON representation of the enclosed array contents. So, it might not return a String of the same format in future releases of Gson.
At any rate, if you really want to use a JsonArray, it should serialize well enough as follows.
JsonElement one = new JsonPrimitive("one");
JsonElement two = new JsonPrimitive(2);
JsonObject foo = new JsonObject();
foo.addProperty("foo", new Foo().foo);
JsonArray jsonArray = new JsonArray();
jsonArray.add(one);
jsonArray.add(two);
jsonArray.add(foo);
System.out.println(new Gson().toJson(jsonArray));
// ["one",2,{"foo":"hi"}]
Note: This answer is based on the Gson 2.2 API. I don't recall whether earlier versions of Gson included the overloaded toJson(JsonElement) methods.
If the toJson method is already being used in this fashion (to serialize a JsonArray), but the output is as demonstrated in the original question, recall that Java doesn't consider the runtime type when selecting amongst overloaded methods. It binds to the compile time type. (Lame -- I know.) So, you may need to cast the argument type to JsonElement, to let the compiler know which method to bind to. The following demonstrates what might be effectively happening in the original question.
System.out.println(new Gson().toJson((Object)jsonArray));
// {"elements":["one",2,{"foo":"hi"}]}
I have this code...
NSData* myData = producedData;
NSLog(#"Contents of myData: %#", myData);
The log prints
{
"id" = "";
"level" = "level_1";
"handle" = test;
}
How do I get the values for id and level and handle out of this? The original data is a NSString*.
Thanks!
Is it JSON? Use Stig Brautaset's JSON parser http://code.google.com/p/json-framework/
You aren't showing the code that actually obtains the data object, nor are you showing any code related to an NSString.
Are you just assigning a string (producedData) to your myData variable? That won't create a data object; for one thing, it wouldn't know what encoding to use to encode the string's characters into bytes, and more importantly, copying a pointer from one variable to another (which is what myData = producedData does—the variables do not contain the objects themselves, only pointers to them) does not change anything about what the pointer points to. The object will remain a string, even though you told the compiler that myData would point to a data object. The compiler should be warning you about this; you should heed and fix those warnings.
myData definitely is not a data object; if it were, its description of itself would be a hex dump. It is either a string or a dictionary.
The output you showed matches the syntax that an NSDictionary uses to describe itself. On the other hand, the object could be a string containing such a description. (This latter case is what you're expecting.)
If you have a dictionary: You're done! The object is already parsed.
If you have a string: Send it a propertyList message, which will parse the string as a property list and return whatever value is represented in it, which, in this case, will be a dictionary.