diff --git a/Sources/SwiftProtobuf/JSONEncodingOptions.swift b/Sources/SwiftProtobuf/JSONEncodingOptions.swift index 8274e08c7..6aa91ed18 100644 --- a/Sources/SwiftProtobuf/JSONEncodingOptions.swift +++ b/Sources/SwiftProtobuf/JSONEncodingOptions.swift @@ -21,6 +21,7 @@ public struct JSONEncodingOptions { /// Whether to preserve proto field names. /// By default they are converted to JSON(lowerCamelCase) names. public var preserveProtoFieldNames: Bool = false - + + public var preserveDefaultValues: Bool = false public init() {} } diff --git a/Sources/SwiftProtobuf/JSONEncodingVisitor.swift b/Sources/SwiftProtobuf/JSONEncodingVisitor.swift index 844bbebd7..a9d36669f 100644 --- a/Sources/SwiftProtobuf/JSONEncodingVisitor.swift +++ b/Sources/SwiftProtobuf/JSONEncodingVisitor.swift @@ -83,56 +83,77 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} try startField(for: fieldNumber) encoder.putFloatValue(value: value) } mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putDoubleValue(value: value) } mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putInt32(value: value) } mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putInt64(value: value) } mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putUInt32(value: value) } mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putUInt64(value: value) } mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putUInt32(value: value) } mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + guard value != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putInt32(value: value) } mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + guard value != false || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putBoolValue(value: value) } mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putStringValue(value: value) } mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.putBytesValue(value: value) } @@ -156,6 +177,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + guard value.rawValue != 0 || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) if !options.alwaysPrintEnumsAsInts, let n = value.name { encoder.appendQuoted(name: n) @@ -175,6 +198,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: Float) in encoder.putFloatValue(value: v) @@ -182,6 +207,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: Double) in encoder.putDoubleValue(value: v) @@ -189,6 +216,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: Int32) in encoder.putInt32(value: v) @@ -196,6 +225,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: Int64) in encoder.putInt64(value: v) @@ -203,6 +234,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: UInt32) in encoder.putUInt32(value: v) @@ -210,6 +243,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: UInt64) in encoder.putUInt64(value: v) @@ -217,30 +252,44 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) } mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) } mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) } mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) } mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) } mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) } mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: Bool) in encoder.putBoolValue(value: v) @@ -248,6 +297,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: String) in encoder.putStringValue(value: v) @@ -255,6 +306,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: Data) in encoder.putBytesValue(value: v) @@ -262,6 +315,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + let alwaysPrintEnumsAsInts = options.alwaysPrintEnumsAsInts try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: E) throws in @@ -274,6 +329,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + let localOptions = options try _visitRepeated(value: value, fieldNumber: fieldNumber) { (encoder: inout JSONEncoder, v: M) throws in @@ -292,6 +349,8 @@ internal struct JSONEncodingVisitor: Visitor { mutating func visitMapField(fieldType: _ProtobufMap.Type, value: _ProtobufMap.BaseType, fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.append(text: "{") var mapVisitor = JSONMapEncodingVisitor(encoder: encoder, options: options) @@ -304,6 +363,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitMapField(fieldType: _ProtobufEnumMap.Type, value: _ProtobufEnumMap.BaseType, fieldNumber: Int) throws where ValueType.RawValue == Int { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.append(text: "{") var mapVisitor = JSONMapEncodingVisitor(encoder: encoder, options: options) @@ -316,6 +377,8 @@ internal struct JSONEncodingVisitor: Visitor { } mutating func visitMapField(fieldType: _ProtobufMessageMap.Type, value: _ProtobufMessageMap.BaseType, fieldNumber: Int) throws { + guard !value.isEmpty || options.preserveDefaultValues else {return} + try startField(for: fieldNumber) encoder.append(text: "{") var mapVisitor = JSONMapEncodingVisitor(encoder: encoder, options: options) diff --git a/Sources/protoc-gen-swift/MessageFieldGenerator.swift b/Sources/protoc-gen-swift/MessageFieldGenerator.swift index 2b6d3147b..168bfe2cc 100644 --- a/Sources/protoc-gen-swift/MessageFieldGenerator.swift +++ b/Sources/protoc-gen-swift/MessageFieldGenerator.swift @@ -196,21 +196,17 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { let varName = hasFieldPresence ? "v" : storedProperty - let conditional: String - if isRepeated { // Also covers maps + var conditional: String? = nil + + if isRepeated && fieldDescriptor.file.syntax != .proto3 { // Also covers maps conditional = "!\(varName).isEmpty" } else if hasFieldPresence { conditional = "let v = \(storedProperty)" - } else { - // At this point, the fields would be a primative type, and should only - // be visted if it is the non default value. - assert(fieldDescriptor.file.syntax == .proto3) - switch fieldDescriptor.type { - case .string, .bytes: - conditional = ("!\(varName).isEmpty") - default: - conditional = ("\(varName) != \(swiftDefaultValue)") - } + } + + guard let conditional = conditional else { + p.print("try visitor.\(visitMethod)(\(traitsArg)value: \(varName), fieldNumber: \(number))\n") + return } p.print("if \(conditional) {\n")