-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Avoid intermediate values when serializing and deserializing proto3 JSON #670
base: master
Are you sure you want to change the base?
Changes from 2 commits
84c6bc8
662efa9
81ddd0c
3594c5d
a64b2f3
a549b57
b8ef37d
fa4d2ea
f155f11
24bffd5
7c92089
6fa98c8
38079b0
a737185
9b62b5d
d44a919
78d495e
c8895c7
e6bee93
add7cce
513d01e
951a2c9
00003ce
48804f1
a5a3f98
dd8cb85
4d43c95
6fe0731
333f16c
a890105
eaa0d35
14bffa2
0e28eb3
02a9960
7899d1f
ee3cf27
617c30c
712cae7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -223,6 +223,13 @@ abstract class GeneratedMessage { | |
{TypeRegistry typeRegistry = const TypeRegistry.empty()}) => | ||
_writeToProto3Json(_fieldSet, typeRegistry); | ||
|
||
String toProto3JsonString( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this really be called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additions in this PR will be inconsistent unless we want to do breaking changes. Currently in master branch (and also in the latest release) we have these generators:
So the proto3 method is already inconsistent. Without changing it, we can name the new methods Should we rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Argh - I see - I introduced that inconsistency - not sure what is the better path forwards.
If we prefer to avoid churn over consistency we keep it as I guess it's up to your judgement. |
||
{TypeRegistry typeRegistry = const TypeRegistry.empty()}) { | ||
StringBuffer buf = StringBuffer(); | ||
_writeToProto3JsonSink(_fieldSet, typeRegistry, buf); | ||
return buf.toString(); | ||
} | ||
|
||
/// Merges field values from [json], a JSON object using proto3 encoding. | ||
/// | ||
/// Well-known types and their special JSON encoding are supported. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
part of protobuf; | ||
|
||
void _writeToProto3JsonSink<S extends StringSink>( | ||
_FieldSet fs, TypeRegistry typeRegistry, S sink) { | ||
// TODO: Either add `writeToProto3JsonSink` for well-known types, or fall | ||
osa1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// back to slow proto3 JSON serialization | ||
if (fs._meta.toProto3Json != null) { | ||
throw 'well-known type detected'; | ||
} | ||
|
||
final JsonWriter<String> jsonWriter = jsonStringWriter(sink, indent: null); | ||
_writeToProto3JsonWriter(fs, typeRegistry, jsonWriter); | ||
} | ||
|
||
void _writeToProto3JsonWriter( | ||
_FieldSet fs, TypeRegistry typeRegistry, JsonWriter<String> jsonWriter) { | ||
jsonWriter.startObject(); // start message | ||
|
||
for (FieldInfo fieldInfo in fs._infosSortedByTag) { | ||
var value = fs._values[fieldInfo.index!]; | ||
|
||
if (value == null || (value is List && value.isEmpty)) { | ||
continue; // It's missing, repeated, or an empty byte array. | ||
} | ||
|
||
jsonWriter.addKey(fieldInfo.name); | ||
|
||
if (fieldInfo.isMapField) { | ||
jsonWriter.startObject(); // start map field | ||
final MapFieldInfo mapEntryInfo = fieldInfo as MapFieldInfo; | ||
for (MapEntry entry in (value as PbMap).entries) { | ||
final key = entry.key; | ||
final value = entry.value; | ||
_writeMapKey(key, mapEntryInfo.keyFieldType, jsonWriter); | ||
_writeFieldValue( | ||
value, mapEntryInfo.valueFieldType, jsonWriter, typeRegistry); | ||
} | ||
jsonWriter.endObject(); // end map field | ||
} else if (fieldInfo.isRepeated) { | ||
jsonWriter.startArray(); // start repeated field | ||
for (final element in value as PbListBase) { | ||
_writeFieldValue(element, fieldInfo.type, jsonWriter, typeRegistry); | ||
} | ||
jsonWriter.endArray(); // end repeated field | ||
} else { | ||
_writeFieldValue(value, fieldInfo.type, jsonWriter, typeRegistry); | ||
} | ||
} | ||
|
||
jsonWriter.endObject(); // end message | ||
} | ||
|
||
void _writeMapKey(dynamic key, int keyType, JsonWriter<String> jsonWriter) { | ||
var baseType = PbFieldType._baseType(keyType); | ||
|
||
assert(!_isRepeated(keyType)); | ||
|
||
switch (baseType) { | ||
case PbFieldType._BOOL_BIT: | ||
jsonWriter.addBool(key as bool); | ||
break; | ||
case PbFieldType._STRING_BIT: | ||
jsonWriter.addString(key as String); | ||
break; | ||
case PbFieldType._UINT64_BIT: | ||
jsonWriter.addString((key as Int64).toStringUnsigned()); | ||
break; | ||
case PbFieldType._INT32_BIT: | ||
case PbFieldType._SINT32_BIT: | ||
case PbFieldType._UINT32_BIT: | ||
case PbFieldType._FIXED32_BIT: | ||
case PbFieldType._SFIXED32_BIT: | ||
case PbFieldType._INT64_BIT: | ||
case PbFieldType._SINT64_BIT: | ||
case PbFieldType._SFIXED64_BIT: | ||
case PbFieldType._FIXED64_BIT: | ||
jsonWriter.addString(key.toString()); | ||
break; | ||
default: | ||
throw StateError('Not a valid key type $keyType'); | ||
} | ||
} | ||
|
||
void _writeFieldValue(dynamic fieldValue, int fieldType, | ||
JsonWriter<String> jsonWriter, TypeRegistry typeRegistry) { | ||
if (fieldValue == null) { | ||
jsonWriter.addNull(); | ||
return; | ||
} | ||
|
||
if (_isGroupOrMessage(fieldType)) { | ||
_writeToProto3JsonWriter( | ||
(fieldValue as GeneratedMessage)._fieldSet, typeRegistry, jsonWriter); | ||
} else if (_isEnum(fieldType)) { | ||
jsonWriter.addString((fieldValue as ProtobufEnum).name); | ||
} else { | ||
final baseType = PbFieldType._baseType(fieldType); | ||
switch (baseType) { | ||
case PbFieldType._BOOL_BIT: | ||
jsonWriter.addBool(fieldValue); | ||
break; | ||
case PbFieldType._STRING_BIT: | ||
jsonWriter.addString(fieldValue); | ||
break; | ||
case PbFieldType._INT32_BIT: | ||
case PbFieldType._SINT32_BIT: | ||
case PbFieldType._UINT32_BIT: | ||
case PbFieldType._FIXED32_BIT: | ||
case PbFieldType._SFIXED32_BIT: | ||
jsonWriter.addNumber(fieldValue); | ||
break; | ||
case PbFieldType._INT64_BIT: | ||
case PbFieldType._SINT64_BIT: | ||
case PbFieldType._SFIXED64_BIT: | ||
case PbFieldType._FIXED64_BIT: | ||
jsonWriter.addString(fieldValue.toString()); | ||
break; | ||
case PbFieldType._FLOAT_BIT: | ||
case PbFieldType._DOUBLE_BIT: | ||
double value = fieldValue; | ||
if (value.isNaN) { | ||
jsonWriter.addString(nan); | ||
} else if (value.isInfinite) { | ||
jsonWriter.addString(value.isNegative ? negativeInfinity : infinity); | ||
} else { | ||
jsonWriter.addNumber(value); | ||
} | ||
break; | ||
case PbFieldType._UINT64_BIT: | ||
jsonWriter.addString((fieldValue as Int64).toStringUnsigned()); | ||
break; | ||
case PbFieldType._BYTES_BIT: | ||
jsonWriter.addString(base64Encode(fieldValue)); | ||
break; | ||
default: | ||
throw StateError( | ||
'Invariant violation: unexpected value type $fieldType'); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider passing an "indent" value