From 51f288062dceebbb16886752ddeb0a24a8d04956 Mon Sep 17 00:00:00 2001 From: Bryan Duxbury Date: Thu, 1 Oct 2009 20:53:45 +0000 Subject: [PATCH] THRIFT-446. java: PartialDeserialization in Java This patch adds a partialDeserialize method to TDeserializer that allows you to request a specific subfield of the serialized data. git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@820786 13f79535-47bb-0310-9956-ffa450edef68 --- lib/java/build.xml | 2 + .../src/org/apache/thrift/TDeserializer.java | 57 +++++++++++++ .../thrift/test/PartialDeserializeTest.java | 82 +++++++++++++++++++ test/DebugProtoTest.thrift | 6 ++ 4 files changed, 147 insertions(+) create mode 100644 lib/java/test/org/apache/thrift/test/PartialDeserializeTest.java diff --git a/lib/java/build.xml b/lib/java/build.xml index dbbaf6b5..c6269481 100644 --- a/lib/java/build.xml +++ b/lib/java/build.xml @@ -182,6 +182,8 @@ classpathref="test.classpath" failonerror="true" /> + diff --git a/lib/java/src/org/apache/thrift/TDeserializer.java b/lib/java/src/org/apache/thrift/TDeserializer.java index d6dd5d4b..7b7d51dc 100644 --- a/lib/java/src/org/apache/thrift/TDeserializer.java +++ b/lib/java/src/org/apache/thrift/TDeserializer.java @@ -23,7 +23,11 @@ import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.protocol.TProtocolUtil; +import org.apache.thrift.protocol.TType; import org.apache.thrift.transport.TIOStreamTransport; /** @@ -80,6 +84,59 @@ public class TDeserializer { } } + /** + * Deserialize only a single Thrift object (addressed by recursively using field id) + * from a byte record. + * @param record The object to read from + * @param tb The object to read into + * @param fieldIdPath The FieldId's that define a path tb + * @throws TException + */ + public void partialDeserialize(TBase tb, byte[] bytes, int ... fieldIdPath) throws TException { + // if there are no elements in the path, then the user is looking for the + // regular deserialize method + // TODO: it might be nice not to have to do this check every time to save + // some performance. + if (fieldIdPath.length == 0) { + deserialize(tb, bytes); + return; + } + + TProtocol iprot = protocolFactory_.getProtocol( + new TIOStreamTransport( + new ByteArrayInputStream(bytes))); + + // index into field ID path being currently searched for + int curPathIndex = 0; + + iprot.readStructBegin(); + + while (curPathIndex < fieldIdPath.length) { + TField field = iprot.readFieldBegin(); + // we can stop searching if we either see a stop or we go past the field + // id we're looking for (since fields should now be serialized in asc + // order). + if (field.type == TType.STOP || field.id > fieldIdPath[curPathIndex]) { + return; + } + + if (field.id != fieldIdPath[curPathIndex]) { + // Not the field we're looking for. Skip field. + TProtocolUtil.skip(iprot, field.type); + iprot.readFieldEnd(); + } else { + // This field is the next step in the path. Step into field. + curPathIndex++; + if (curPathIndex < fieldIdPath.length) { + iprot.readStructBegin(); + } + } + } + + // when this line is reached, iprot will be positioned at the start of tb. + tb.read(iprot); + } + /** * Deserialize the Thrift object from a Java string, using the default JVM * charset encoding. diff --git a/lib/java/test/org/apache/thrift/test/PartialDeserializeTest.java b/lib/java/test/org/apache/thrift/test/PartialDeserializeTest.java new file mode 100644 index 00000000..d88a686d --- /dev/null +++ b/lib/java/test/org/apache/thrift/test/PartialDeserializeTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package org.apache.thrift.test; + +import org.apache.thrift.TBase; +import org.apache.thrift.TDeserializer; +import org.apache.thrift.TException; +import org.apache.thrift.TSerializer; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.protocol.TProtocolFactory; + +import thrift.test.Backwards; +import thrift.test.OneOfEach; +import thrift.test.PrimitiveThenStruct; +import thrift.test.StructWithAUnion; +import thrift.test.TestUnion; + +public class PartialDeserializeTest { + + private static final TProtocolFactory[] PROTOCOLS = new TProtocolFactory[] { + new TBinaryProtocol.Factory(), + new TCompactProtocol.Factory(), + new TJSONProtocol.Factory() + }; + + public static void main(String[] args) throws TException { + //Root:StructWithAUnion + // 1:Union + // 1.3:OneOfEach + OneOfEach Level3OneOfEach = Fixtures.oneOfEach; + TestUnion Level2TestUnion = new TestUnion(TestUnion.STRUCT_FIELD, Level3OneOfEach); + StructWithAUnion Level1SWU = new StructWithAUnion(Level2TestUnion); + + Backwards bw = new Backwards(2, 1); + PrimitiveThenStruct pts = new PrimitiveThenStruct(12345, 67890, bw); + + for (TProtocolFactory factory : PROTOCOLS) { + //Full deserialization test + testPartialDeserialize(factory, Level1SWU, new StructWithAUnion(), Level1SWU); + + //Level 2 test + testPartialDeserialize(factory, Level1SWU, new TestUnion(), Level2TestUnion, StructWithAUnion.TEST_UNION); + + //Level 3 on 3rd field test + testPartialDeserialize(factory, Level1SWU, new OneOfEach(), Level3OneOfEach, StructWithAUnion.TEST_UNION, TestUnion.STRUCT_FIELD); + + //Test early termination when traversed path Field.id exceeds the one being searched for + testPartialDeserialize(factory, Level1SWU, new OneOfEach(), new OneOfEach(), StructWithAUnion.TEST_UNION, TestUnion.I32_FIELD); + + //Test that readStructBegin isn't called on primitive + testPartialDeserialize(factory, pts, new Backwards(), bw, PrimitiveThenStruct.BW); + } + } + + public static void testPartialDeserialize(TProtocolFactory protocolFactory, TBase input, TBase output, TBase expected, int ... fieldIdPath) throws TException { + byte[] record = new TSerializer(protocolFactory).serialize(input); + new TDeserializer(protocolFactory).partialDeserialize(output, record, fieldIdPath); + if(!output.equals(expected)) + throw new RuntimeException("with " + protocolFactory.toString() + ", expected " + expected + " but got " + output); + } +} + diff --git a/test/DebugProtoTest.thrift b/test/DebugProtoTest.thrift index 9ba60a26..9b3952e6 100644 --- a/test/DebugProtoTest.thrift +++ b/test/DebugProtoTest.thrift @@ -265,4 +265,10 @@ union TestUnion { struct StructWithAUnion { 1: TestUnion test_union; +} + +struct PrimitiveThenStruct { + 1: i32 blah; + 2: i32 blah2; + 3: Backwards bw; } \ No newline at end of file -- 2.17.1