|
| 1 | +diff --git a/lib/protobuf/field/bytes_field.rb b/lib/protobuf/field/bytes_field.rb |
| 2 | +index 81a3634..b8d8807 100644 |
| 3 | +--- a/lib/protobuf/field/bytes_field.rb |
| 4 | ++++ b/lib/protobuf/field/bytes_field.rb |
| 5 | +@@ -59,7 +59,7 @@ module Protobuf |
| 6 | + end |
| 7 | + end |
| 8 | + |
| 9 | +- def json_encode(value) |
| 10 | ++ def json_encode(value, options={}) |
| 11 | + Base64.strict_encode64(value) |
| 12 | + end |
| 13 | + end |
| 14 | +diff --git a/lib/protobuf/field/field_array.rb b/lib/protobuf/field/field_array.rb |
| 15 | +index e4f2eb1..eb1f29d 100644 |
| 16 | +--- a/lib/protobuf/field/field_array.rb |
| 17 | ++++ b/lib/protobuf/field/field_array.rb |
| 18 | +@@ -49,14 +49,14 @@ module Protobuf |
| 19 | + # Return a hash-representation of the given values for this field type |
| 20 | + # that is safe to convert to JSON. |
| 21 | + # The value in this case would be an array. |
| 22 | +- def to_json_hash_value |
| 23 | ++ def to_json_hash_value(options = {}) |
| 24 | + if field.respond_to?(:json_encode) |
| 25 | + map do |value| |
| 26 | + field.json_encode(value) |
| 27 | + end |
| 28 | + else |
| 29 | + map do |value| |
| 30 | +- value.respond_to?(:to_json_hash_value) ? value.to_json_hash_value : value |
| 31 | ++ value.respond_to?(:to_json_hash_value) ? value.to_json_hash_value(options) : value |
| 32 | + end |
| 33 | + end |
| 34 | + end |
| 35 | +diff --git a/lib/protobuf/field/field_hash.rb b/lib/protobuf/field/field_hash.rb |
| 36 | +index 36b2644..94eedbb 100644 |
| 37 | +--- a/lib/protobuf/field/field_hash.rb |
| 38 | ++++ b/lib/protobuf/field/field_hash.rb |
| 39 | +@@ -58,14 +58,14 @@ module Protobuf |
| 40 | + # The value in this case would be the hash itself, right? Unfortunately |
| 41 | + # not because the values of the map could be messages themselves that we |
| 42 | + # need to transform. |
| 43 | +- def to_json_hash_value |
| 44 | ++ def to_json_hash_value(options = {}) |
| 45 | + if field.respond_to?(:json_encode) |
| 46 | + each_with_object({}) do |(key, value), hash| |
| 47 | + hash[key] = field.json_encode(value) |
| 48 | + end |
| 49 | + else |
| 50 | + each_with_object({}) do |(key, value), hash| |
| 51 | +- hash[key] = value.respond_to?(:to_json_hash_value) ? value.to_json_hash_value : value |
| 52 | ++ hash[key] = value.respond_to?(:to_json_hash_value) ? value.to_json_hash_value(options) : value |
| 53 | + end |
| 54 | + end |
| 55 | + end |
| 56 | +diff --git a/lib/protobuf/field/int64_field.rb b/lib/protobuf/field/int64_field.rb |
| 57 | +index 3b33889..f2597b2 100644 |
| 58 | +--- a/lib/protobuf/field/int64_field.rb |
| 59 | ++++ b/lib/protobuf/field/int64_field.rb |
| 60 | +@@ -29,6 +29,13 @@ module Protobuf |
| 61 | + return false |
| 62 | + end |
| 63 | + |
| 64 | ++ def json_encode(value, options = {}) |
| 65 | ++ if options[:proto3] |
| 66 | ++ value == 0 ? nil : value.to_s |
| 67 | ++ else |
| 68 | ++ value |
| 69 | ++ end |
| 70 | ++ end |
| 71 | + end |
| 72 | + end |
| 73 | + end |
| 74 | +diff --git a/lib/protobuf/field/sint64_field.rb b/lib/protobuf/field/sint64_field.rb |
| 75 | +index 2aba7df..0c1c085 100644 |
| 76 | +--- a/lib/protobuf/field/sint64_field.rb |
| 77 | ++++ b/lib/protobuf/field/sint64_field.rb |
| 78 | +@@ -16,6 +16,13 @@ module Protobuf |
| 79 | + INT64_MIN |
| 80 | + end |
| 81 | + |
| 82 | ++ def json_encode(value, options = {}) |
| 83 | ++ if options[:proto3] |
| 84 | ++ value == 0 ? nil : value.to_s |
| 85 | ++ else |
| 86 | ++ value |
| 87 | ++ end |
| 88 | ++ end |
| 89 | + end |
| 90 | + end |
| 91 | + end |
| 92 | +diff --git a/lib/protobuf/field/string_field.rb b/lib/protobuf/field/string_field.rb |
| 93 | +index 6c9c278..551bdd2 100644 |
| 94 | +--- a/lib/protobuf/field/string_field.rb |
| 95 | ++++ b/lib/protobuf/field/string_field.rb |
| 96 | +@@ -43,7 +43,7 @@ module Protobuf |
| 97 | + "#{::Protobuf::Field::VarintField.encode(value_to_encode.bytesize)}#{value_to_encode}" |
| 98 | + end |
| 99 | + |
| 100 | +- def json_encode(value) |
| 101 | ++ def json_encode(value, options={}) |
| 102 | + value |
| 103 | + end |
| 104 | + end |
| 105 | +diff --git a/lib/protobuf/field/uint64_field.rb b/lib/protobuf/field/uint64_field.rb |
| 106 | +index 8a060f1..df041fc 100644 |
| 107 | +--- a/lib/protobuf/field/uint64_field.rb |
| 108 | ++++ b/lib/protobuf/field/uint64_field.rb |
| 109 | +@@ -16,6 +16,13 @@ module Protobuf |
| 110 | + 0 |
| 111 | + end |
| 112 | + |
| 113 | ++ def json_encode(value, options = {}) |
| 114 | ++ if options[:proto3] |
| 115 | ++ value == 0 ? nil : value.to_s |
| 116 | ++ else |
| 117 | ++ value |
| 118 | ++ end |
| 119 | ++ end |
| 120 | + end |
| 121 | + end |
| 122 | + end |
| 123 | +diff --git a/lib/protobuf/message.rb b/lib/protobuf/message.rb |
| 124 | +index a13c0d1..87e1556 100644 |
| 125 | +--- a/lib/protobuf/message.rb |
| 126 | ++++ b/lib/protobuf/message.rb |
| 127 | +@@ -134,14 +134,16 @@ module Protobuf |
| 128 | + end |
| 129 | + |
| 130 | + def to_json(options = {}) |
| 131 | +- to_json_hash.to_json(options) |
| 132 | ++ to_json_hash(options).to_json(options) |
| 133 | + end |
| 134 | + |
| 135 | + # Return a hash-representation of the given fields for this message type that |
| 136 | + # is safe to convert to JSON. |
| 137 | +- def to_json_hash |
| 138 | ++ def to_json_hash(options = {}) |
| 139 | + result = {} |
| 140 | + |
| 141 | ++ proto3 = options[:proto3] || options[:lower_camel_case] |
| 142 | ++ |
| 143 | + @values.each_key do |field_name| |
| 144 | + value = self[field_name] |
| 145 | + field = self.class.get_field(field_name, true) |
| 146 | +@@ -149,14 +151,19 @@ module Protobuf |
| 147 | + # NB: to_json_hash_value should come before json_encode so as to handle |
| 148 | + # repeated fields without extra logic. |
| 149 | + hashed_value = if value.respond_to?(:to_json_hash_value) |
| 150 | +- value.to_json_hash_value |
| 151 | ++ value.to_json_hash_value(options) |
| 152 | + elsif field.respond_to?(:json_encode) |
| 153 | +- field.json_encode(value) |
| 154 | ++ field.json_encode(value, options) |
| 155 | + else |
| 156 | + value |
| 157 | + end |
| 158 | + |
| 159 | +- result[field.name] = hashed_value |
| 160 | ++ if proto3 && (hashed_value.nil? || value == field.class.default) |
| 161 | ++ result.delete(field.name) |
| 162 | ++ else |
| 163 | ++ key = proto3 ? field.name.to_s.camelize(:lower).to_sym : field.name |
| 164 | ++ result[key] = hashed_value |
| 165 | ++ end |
| 166 | + end |
| 167 | + |
| 168 | + result |
| 169 | +diff --git a/spec/lib/protobuf/field/fixed64_field_spec.rb b/spec/lib/protobuf/field/fixed64_field_spec.rb |
| 170 | +index d7feb12..00ad743 100644 |
| 171 | +--- a/spec/lib/protobuf/field/fixed64_field_spec.rb |
| 172 | ++++ b/spec/lib/protobuf/field/fixed64_field_spec.rb |
| 173 | +@@ -4,4 +4,30 @@ RSpec.describe Protobuf::Field::Fixed64Field do |
| 174 | + |
| 175 | + it_behaves_like :packable_field, described_class |
| 176 | + |
| 177 | ++ let(:message) do |
| 178 | ++ Class.new(::Protobuf::Message) do |
| 179 | ++ optional :fixed64, :some_field, 1 |
| 180 | ++ end |
| 181 | ++ end |
| 182 | ++ |
| 183 | ++ # https://developers.google.com/protocol-buffers/docs/proto3#json |
| 184 | ++ describe '.{to_json, from_json}' do |
| 185 | ++ it 'serialises 0' do |
| 186 | ++ instance = message.new(some_field: 0) |
| 187 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 188 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 189 | ++ end |
| 190 | ++ |
| 191 | ++ it 'serialises max value' do |
| 192 | ++ instance = message.new(some_field: described_class.max) |
| 193 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"18446744073709551615"}') |
| 194 | ++ expect(instance.to_json).to eq('{"some_field":18446744073709551615}') |
| 195 | ++ end |
| 196 | ++ |
| 197 | ++ it 'serialises min value' do |
| 198 | ++ instance = message.new(some_field: described_class.min) |
| 199 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 200 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 201 | ++ end |
| 202 | ++ end |
| 203 | + end |
| 204 | +diff --git a/spec/lib/protobuf/field/int64_field_spec.rb b/spec/lib/protobuf/field/int64_field_spec.rb |
| 205 | +index 1bbe7c0..d0c77d3 100644 |
| 206 | +--- a/spec/lib/protobuf/field/int64_field_spec.rb |
| 207 | ++++ b/spec/lib/protobuf/field/int64_field_spec.rb |
| 208 | +@@ -4,4 +4,30 @@ RSpec.describe Protobuf::Field::Int64Field do |
| 209 | + |
| 210 | + it_behaves_like :packable_field, described_class |
| 211 | + |
| 212 | ++ let(:message) do |
| 213 | ++ Class.new(::Protobuf::Message) do |
| 214 | ++ optional :int64, :some_field, 1 |
| 215 | ++ end |
| 216 | ++ end |
| 217 | ++ |
| 218 | ++ # https://developers.google.com/protocol-buffers/docs/proto3#json |
| 219 | ++ describe '.{to_json, from_json}' do |
| 220 | ++ it 'serialises 0' do |
| 221 | ++ instance = message.new(some_field: 0) |
| 222 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 223 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 224 | ++ end |
| 225 | ++ |
| 226 | ++ it 'serialises max value' do |
| 227 | ++ instance = message.new(some_field: described_class.max) |
| 228 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"9223372036854775807"}') |
| 229 | ++ expect(instance.to_json).to eq('{"some_field":9223372036854775807}') |
| 230 | ++ end |
| 231 | ++ |
| 232 | ++ it 'serialises min value' do |
| 233 | ++ instance = message.new(some_field: described_class.min) |
| 234 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"-9223372036854775808"}') |
| 235 | ++ expect(instance.to_json).to eq('{"some_field":-9223372036854775808}') |
| 236 | ++ end |
| 237 | ++ end |
| 238 | + end |
| 239 | +diff --git a/spec/lib/protobuf/field/sfixed64_field_spec.rb b/spec/lib/protobuf/field/sfixed64_field_spec.rb |
| 240 | +index f380ed5..7988f45 100644 |
| 241 | +--- a/spec/lib/protobuf/field/sfixed64_field_spec.rb |
| 242 | ++++ b/spec/lib/protobuf/field/sfixed64_field_spec.rb |
| 243 | +@@ -6,4 +6,30 @@ RSpec.describe Protobuf::Field::Sfixed64Field do |
| 244 | + let(:value) { [-1, 0, 1] } |
| 245 | + end |
| 246 | + |
| 247 | ++ let(:message) do |
| 248 | ++ Class.new(::Protobuf::Message) do |
| 249 | ++ optional :sfixed64, :some_field, 1 |
| 250 | ++ end |
| 251 | ++ end |
| 252 | ++ |
| 253 | ++ # https://developers.google.com/protocol-buffers/docs/proto3#json |
| 254 | ++ describe '.{to_json, from_json}' do |
| 255 | ++ it 'serialises 0' do |
| 256 | ++ instance = message.new(some_field: 0) |
| 257 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 258 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 259 | ++ end |
| 260 | ++ |
| 261 | ++ it 'serialises max value' do |
| 262 | ++ instance = message.new(some_field: described_class.max) |
| 263 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"9223372036854775807"}') |
| 264 | ++ expect(instance.to_json).to eq('{"some_field":9223372036854775807}') |
| 265 | ++ end |
| 266 | ++ |
| 267 | ++ it 'serialises min value as string' do |
| 268 | ++ instance = message.new(some_field: described_class.min) |
| 269 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"-9223372036854775808"}') |
| 270 | ++ expect(instance.to_json).to eq('{"some_field":-9223372036854775808}') |
| 271 | ++ end |
| 272 | ++ end |
| 273 | + end |
| 274 | +diff --git a/spec/lib/protobuf/field/sint64_field_spec.rb b/spec/lib/protobuf/field/sint64_field_spec.rb |
| 275 | +index 84ca730..e6fc05f 100644 |
| 276 | +--- a/spec/lib/protobuf/field/sint64_field_spec.rb |
| 277 | ++++ b/spec/lib/protobuf/field/sint64_field_spec.rb |
| 278 | +@@ -6,4 +6,30 @@ RSpec.describe Protobuf::Field::Sint64Field do |
| 279 | + let(:value) { [-1, 0, 1] } |
| 280 | + end |
| 281 | + |
| 282 | ++ let(:message) do |
| 283 | ++ Class.new(::Protobuf::Message) do |
| 284 | ++ optional :sint64, :some_field, 1 |
| 285 | ++ end |
| 286 | ++ end |
| 287 | ++ |
| 288 | ++ # https://developers.google.com/protocol-buffers/docs/proto3#json |
| 289 | ++ describe '.{to_json, from_json}' do |
| 290 | ++ it 'serialises 0' do |
| 291 | ++ instance = message.new(some_field: 0) |
| 292 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 293 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 294 | ++ end |
| 295 | ++ |
| 296 | ++ it 'serialises max value as string' do |
| 297 | ++ instance = message.new(some_field: described_class.max) |
| 298 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"9223372036854775807"}') |
| 299 | ++ expect(instance.to_json).to eq('{"some_field":9223372036854775807}') |
| 300 | ++ end |
| 301 | ++ |
| 302 | ++ it 'serialises min value as string' do |
| 303 | ++ instance = message.new(some_field: described_class.min) |
| 304 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"-9223372036854775808"}') |
| 305 | ++ expect(instance.to_json).to eq('{"some_field":-9223372036854775808}') |
| 306 | ++ end |
| 307 | ++ end |
| 308 | + end |
| 309 | +diff --git a/spec/lib/protobuf/field/uint64_field_spec.rb b/spec/lib/protobuf/field/uint64_field_spec.rb |
| 310 | +index 61b8d1b..90bc030 100644 |
| 311 | +--- a/spec/lib/protobuf/field/uint64_field_spec.rb |
| 312 | ++++ b/spec/lib/protobuf/field/uint64_field_spec.rb |
| 313 | +@@ -4,4 +4,30 @@ RSpec.describe Protobuf::Field::Uint64Field do |
| 314 | + |
| 315 | + it_behaves_like :packable_field, described_class |
| 316 | + |
| 317 | ++ let(:message) do |
| 318 | ++ Class.new(::Protobuf::Message) do |
| 319 | ++ optional :uint64, :some_field, 1 |
| 320 | ++ end |
| 321 | ++ end |
| 322 | ++ |
| 323 | ++ # https://developers.google.com/protocol-buffers/docs/proto3#json |
| 324 | ++ describe '.{to_json, from_json}' do |
| 325 | ++ it 'serialises 0' do |
| 326 | ++ instance = message.new(some_field: 0) |
| 327 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 328 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 329 | ++ end |
| 330 | ++ |
| 331 | ++ it 'serialises max value' do |
| 332 | ++ instance = message.new(some_field: described_class.max) |
| 333 | ++ expect(instance.to_json(proto3: true)).to eq('{"someField":"18446744073709551615"}') |
| 334 | ++ expect(instance.to_json).to eq('{"some_field":18446744073709551615}') |
| 335 | ++ end |
| 336 | ++ |
| 337 | ++ it 'serialises min value' do |
| 338 | ++ instance = message.new(some_field: described_class.min) |
| 339 | ++ expect(instance.to_json(proto3: true)).to eq('{}') |
| 340 | ++ expect(instance.to_json).to eq('{"some_field":0}') |
| 341 | ++ end |
| 342 | ++ end |
| 343 | + end |
| 344 | +diff --git a/spec/lib/protobuf/message_spec.rb b/spec/lib/protobuf/message_spec.rb |
| 345 | +index 96668b6..c6e9720 100644 |
| 346 | +--- a/spec/lib/protobuf/message_spec.rb |
| 347 | ++++ b/spec/lib/protobuf/message_spec.rb |
| 348 | +@@ -437,6 +437,16 @@ RSpec.describe Protobuf::Message do |
| 349 | + |
| 350 | + specify { expect(subject.to_json).to eq '{"widget_bytes":["Bo0xSFAXOmI="]}' } |
| 351 | + end |
| 352 | ++ |
| 353 | ++ context 'using proto3 produces lower case field names' do |
| 354 | ++ let(:bytes) { "\x06\x8D1HP\x17:b" } |
| 355 | ++ |
| 356 | ++ subject do |
| 357 | ++ ::Test::ResourceFindRequest.new(:widget_bytes => [bytes]) |
| 358 | ++ end |
| 359 | ++ |
| 360 | ++ specify { expect(subject.to_json(:proto3 => true)).to eq '{"widgetBytes":["Bo0xSFAXOmI="]}' } |
| 361 | ++ end |
| 362 | + end |
| 363 | + |
| 364 | + describe '.to_json' do |
0 commit comments