UnknownFieldSet.parseFrom(msg).toString()
Это покажет вам поля верхнего уровня.К сожалению, он не может знать точные детали типов полей.long / int / bool / enum и т. д. все они закодированы как Varint и выглядят одинаково.Строки, байтовые массивы и вложенные сообщения разделяются по длине и также неразличимы.
Некоторые полезные детали здесь: https://github.com/dcodeIO/protobuf.js/wiki/How-to-reverse-engineer-a-buffer-by-hand
Если вы будете следовать коду в UnknownFieldSet.mergeFrom (), вы увидите, как вы можете попытаться декодировать вложенные сообщения и вернуться к строкам, еслиэто терпит неудачу - но это не будет очень надежно.
В протоколе есть 2 запасных значения для wiretype - было бы действительно полезно, если бы Google использовал одно из них для обозначения вложенных сообщений.(И, возможно, другое для нулевых значений.)
Вот некоторый очень грубый поспешный код, который пытается произвести что-то полезное для диагностики.Он догадывается о типах данных, а в случае строк и вложенных сообщений он печатает обе альтернативы в некоторых случаях.Пожалуйста, не доверяйте никаким значениям, которые он печатает:
public static String decodeProto(byte[] data, boolean singleLine) throws IOException {
return decodeProto(ByteString.copyFrom(data), 0, singleLine);
}
public static String decodeProto(ByteString data, int depth, boolean singleLine) throws IOException {
final CodedInputStream input = CodedInputStream.newInstance(data.asReadOnlyByteBuffer());
return decodeProtoInput(input, depth, singleLine);
}
private static String decodeProtoInput(CodedInputStream input, int depth, boolean singleLine) throws IOException {
StringBuilder s = new StringBuilder("{ ");
boolean foundFields = false;
while (true) {
final int tag = input.readTag();
int type = WireFormat.getTagWireType(tag);
if (tag == 0 || type == WireFormat.WIRETYPE_END_GROUP) {
break;
}
foundFields = true;
protoNewline(depth, s, singleLine);
final int number = WireFormat.getTagFieldNumber(tag);
s.append(number).append(": ");
switch (type) {
case WireFormat.WIRETYPE_VARINT:
s.append(input.readInt64());
break;
case WireFormat.WIRETYPE_FIXED64:
s.append(Double.longBitsToDouble(input.readFixed64()));
break;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
ByteString data = input.readBytes();
try {
String submessage = decodeProto(data, depth + 1, singleLine);
if (data.size() < 30) {
boolean probablyString = true;
String str = new String(data.toByteArray(), Charsets.UTF_8);
for (char c : str.toCharArray()) {
if (c < '\n') {
probablyString = false;
break;
}
}
if (probablyString) {
s.append("\"").append(str).append("\" ");
}
}
s.append(submessage);
} catch (IOException e) {
s.append('"').append(new String(data.toByteArray())).append('"');
}
break;
case WireFormat.WIRETYPE_START_GROUP:
s.append(decodeProtoInput(input, depth + 1, singleLine));
break;
case WireFormat.WIRETYPE_FIXED32:
s.append(Float.intBitsToFloat(input.readFixed32()));
break;
default:
throw new InvalidProtocolBufferException("Invalid wire type");
}
}
if (foundFields) {
protoNewline(depth - 1, s, singleLine);
}
return s.append('}').toString();
}
private static void protoNewline(int depth, StringBuilder s, boolean noNewline) {
if (noNewline) {
s.append(" ");
return;
}
s.append('\n');
for (int i = 0; i <= depth; i++) {
s.append(INDENT);
}
}