diff --git a/lib/protobuf/field/bytes_field.rb b/lib/protobuf/field/bytes_field.rb index 81a3634d..b8d88078 100644 --- a/lib/protobuf/field/bytes_field.rb +++ b/lib/protobuf/field/bytes_field.rb @@ -59,7 +59,7 @@ def coerce!(value) end end - def json_encode(value) + def json_encode(value, options={}) Base64.strict_encode64(value) end end diff --git a/lib/protobuf/field/int64_field.rb b/lib/protobuf/field/int64_field.rb index 3b338894..f2597b25 100644 --- a/lib/protobuf/field/int64_field.rb +++ b/lib/protobuf/field/int64_field.rb @@ -29,6 +29,13 @@ def acceptable?(val) return false end + def json_encode(value, options = {}) + if options[:proto3] + value == 0 ? nil : value.to_s + else + value + end + end end end end diff --git a/lib/protobuf/field/sint64_field.rb b/lib/protobuf/field/sint64_field.rb index 2aba7dfa..0c1c0856 100644 --- a/lib/protobuf/field/sint64_field.rb +++ b/lib/protobuf/field/sint64_field.rb @@ -16,6 +16,13 @@ def self.min INT64_MIN end + def json_encode(value, options = {}) + if options[:proto3] + value == 0 ? nil : value.to_s + else + value + end + end end end end diff --git a/lib/protobuf/field/string_field.rb b/lib/protobuf/field/string_field.rb index 6c9c278f..551bdd23 100644 --- a/lib/protobuf/field/string_field.rb +++ b/lib/protobuf/field/string_field.rb @@ -43,7 +43,7 @@ def encode(value) "#{::Protobuf::Field::VarintField.encode(value_to_encode.bytesize)}#{value_to_encode}" end - def json_encode(value) + def json_encode(value, options={}) value end end diff --git a/lib/protobuf/field/uint64_field.rb b/lib/protobuf/field/uint64_field.rb index 8a060f14..df041fc5 100644 --- a/lib/protobuf/field/uint64_field.rb +++ b/lib/protobuf/field/uint64_field.rb @@ -16,6 +16,13 @@ def self.min 0 end + def json_encode(value, options = {}) + if options[:proto3] + value == 0 ? nil : value.to_s + else + value + end + end end end end diff --git a/lib/protobuf/message.rb b/lib/protobuf/message.rb index 06b26b94..87e15566 100644 --- a/lib/protobuf/message.rb +++ b/lib/protobuf/message.rb @@ -142,7 +142,7 @@ def to_json(options = {}) def to_json_hash(options = {}) result = {} - lower_camel_case = options[:lower_camel_case] + proto3 = options[:proto3] || options[:lower_camel_case] @values.each_key do |field_name| value = self[field_name] @@ -153,13 +153,17 @@ def to_json_hash(options = {}) hashed_value = if value.respond_to?(:to_json_hash_value) value.to_json_hash_value(options) elsif field.respond_to?(:json_encode) - field.json_encode(value) + field.json_encode(value, options) else value end - key = lower_camel_case ? field.name.to_s.camelize(:lower).to_sym : field.name - result[key] = hashed_value + if proto3 && (hashed_value.nil? || value == field.class.default) + result.delete(field.name) + else + key = proto3 ? field.name.to_s.camelize(:lower).to_sym : field.name + result[key] = hashed_value + end end result diff --git a/spec/lib/protobuf/field/fixed64_field_spec.rb b/spec/lib/protobuf/field/fixed64_field_spec.rb index d7feb120..00ad743a 100644 --- a/spec/lib/protobuf/field/fixed64_field_spec.rb +++ b/spec/lib/protobuf/field/fixed64_field_spec.rb @@ -4,4 +4,30 @@ it_behaves_like :packable_field, described_class + let(:message) do + Class.new(::Protobuf::Message) do + optional :fixed64, :some_field, 1 + end + end + + # https://developers.google.com/protocol-buffers/docs/proto3#json + describe '.{to_json, from_json}' do + it 'serialises 0' do + instance = message.new(some_field: 0) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + + it 'serialises max value' do + instance = message.new(some_field: described_class.max) + expect(instance.to_json(proto3: true)).to eq('{"someField":"18446744073709551615"}') + expect(instance.to_json).to eq('{"some_field":18446744073709551615}') + end + + it 'serialises min value' do + instance = message.new(some_field: described_class.min) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + end end diff --git a/spec/lib/protobuf/field/int64_field_spec.rb b/spec/lib/protobuf/field/int64_field_spec.rb index 1bbe7c04..d0c77d34 100644 --- a/spec/lib/protobuf/field/int64_field_spec.rb +++ b/spec/lib/protobuf/field/int64_field_spec.rb @@ -4,4 +4,30 @@ it_behaves_like :packable_field, described_class + let(:message) do + Class.new(::Protobuf::Message) do + optional :int64, :some_field, 1 + end + end + + # https://developers.google.com/protocol-buffers/docs/proto3#json + describe '.{to_json, from_json}' do + it 'serialises 0' do + instance = message.new(some_field: 0) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + + it 'serialises max value' do + instance = message.new(some_field: described_class.max) + expect(instance.to_json(proto3: true)).to eq('{"someField":"9223372036854775807"}') + expect(instance.to_json).to eq('{"some_field":9223372036854775807}') + end + + it 'serialises min value' do + instance = message.new(some_field: described_class.min) + expect(instance.to_json(proto3: true)).to eq('{"someField":"-9223372036854775808"}') + expect(instance.to_json).to eq('{"some_field":-9223372036854775808}') + end + end end diff --git a/spec/lib/protobuf/field/sfixed64_field_spec.rb b/spec/lib/protobuf/field/sfixed64_field_spec.rb index f380ed5d..7988f45f 100644 --- a/spec/lib/protobuf/field/sfixed64_field_spec.rb +++ b/spec/lib/protobuf/field/sfixed64_field_spec.rb @@ -6,4 +6,30 @@ let(:value) { [-1, 0, 1] } end + let(:message) do + Class.new(::Protobuf::Message) do + optional :sfixed64, :some_field, 1 + end + end + + # https://developers.google.com/protocol-buffers/docs/proto3#json + describe '.{to_json, from_json}' do + it 'serialises 0' do + instance = message.new(some_field: 0) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + + it 'serialises max value' do + instance = message.new(some_field: described_class.max) + expect(instance.to_json(proto3: true)).to eq('{"someField":"9223372036854775807"}') + expect(instance.to_json).to eq('{"some_field":9223372036854775807}') + end + + it 'serialises min value as string' do + instance = message.new(some_field: described_class.min) + expect(instance.to_json(proto3: true)).to eq('{"someField":"-9223372036854775808"}') + expect(instance.to_json).to eq('{"some_field":-9223372036854775808}') + end + end end diff --git a/spec/lib/protobuf/field/sint64_field_spec.rb b/spec/lib/protobuf/field/sint64_field_spec.rb index 84ca7304..e6fc05ff 100644 --- a/spec/lib/protobuf/field/sint64_field_spec.rb +++ b/spec/lib/protobuf/field/sint64_field_spec.rb @@ -6,4 +6,30 @@ let(:value) { [-1, 0, 1] } end + let(:message) do + Class.new(::Protobuf::Message) do + optional :sint64, :some_field, 1 + end + end + + # https://developers.google.com/protocol-buffers/docs/proto3#json + describe '.{to_json, from_json}' do + it 'serialises 0' do + instance = message.new(some_field: 0) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + + it 'serialises max value as string' do + instance = message.new(some_field: described_class.max) + expect(instance.to_json(proto3: true)).to eq('{"someField":"9223372036854775807"}') + expect(instance.to_json).to eq('{"some_field":9223372036854775807}') + end + + it 'serialises min value as string' do + instance = message.new(some_field: described_class.min) + expect(instance.to_json(proto3: true)).to eq('{"someField":"-9223372036854775808"}') + expect(instance.to_json).to eq('{"some_field":-9223372036854775808}') + end + end end diff --git a/spec/lib/protobuf/field/uint64_field_spec.rb b/spec/lib/protobuf/field/uint64_field_spec.rb index 61b8d1b9..90bc0306 100644 --- a/spec/lib/protobuf/field/uint64_field_spec.rb +++ b/spec/lib/protobuf/field/uint64_field_spec.rb @@ -4,4 +4,30 @@ it_behaves_like :packable_field, described_class + let(:message) do + Class.new(::Protobuf::Message) do + optional :uint64, :some_field, 1 + end + end + + # https://developers.google.com/protocol-buffers/docs/proto3#json + describe '.{to_json, from_json}' do + it 'serialises 0' do + instance = message.new(some_field: 0) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + + it 'serialises max value' do + instance = message.new(some_field: described_class.max) + expect(instance.to_json(proto3: true)).to eq('{"someField":"18446744073709551615"}') + expect(instance.to_json).to eq('{"some_field":18446744073709551615}') + end + + it 'serialises min value' do + instance = message.new(some_field: described_class.min) + expect(instance.to_json(proto3: true)).to eq('{}') + expect(instance.to_json).to eq('{"some_field":0}') + end + end end diff --git a/spec/lib/protobuf/message_spec.rb b/spec/lib/protobuf/message_spec.rb index 13110bc9..c6e9720d 100644 --- a/spec/lib/protobuf/message_spec.rb +++ b/spec/lib/protobuf/message_spec.rb @@ -438,14 +438,14 @@ specify { expect(subject.to_json).to eq '{"widget_bytes":["Bo0xSFAXOmI="]}' } end - context 'using lower camel case field names' do + context 'using proto3 produces lower case field names' do let(:bytes) { "\x06\x8D1HP\x17:b" } subject do ::Test::ResourceFindRequest.new(:widget_bytes => [bytes]) end - specify { expect(subject.to_json(:lower_camel_case => true)).to eq '{"widgetBytes":["Bo0xSFAXOmI="]}' } + specify { expect(subject.to_json(:proto3 => true)).to eq '{"widgetBytes":["Bo0xSFAXOmI="]}' } end end