序列化RPC-GWT [英] Serializing RPC-GWT

查看:196
本文介绍了序列化RPC-GWT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我需要的是将一些数据放在网页页面表单。这是我手动执行时发送的内容:


7 | 0 | 48 | https://aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/|03152A2DEBABDCE5D33BF4C88511DD1E|net.customware.gwt.dispatch.client.standard.StandardDispatchService|execute|net.customware.gwt.dispatch.shared。动作| gov.sena sa.embalajemadera.shared.rpc.actions.IngresarDeclaracionJuradaAction / 2514804035 | gov.senasa.embalajemadera.shared.domain.DeclaracionJurada / 1628723960 | java.util.ArrayList中/ 415975576​​0 | gov.senasa.embalajemadera.shared.domain.TipoEmbalajeCantidad / 4152068152 | java.lang.Integer中/ 3438268394 |托盘| gov.senasa.embalajemadera.shared.domain.TipoEmbalaje / 309031988 | java.util.HashSet中/ 3273092938 | gov.senasa.embalajemadera.shared.domain.Contenedor / 1178264080 | NRO contenedor 1 | java.lang.Boolean / 476441737 | gov.senasa.embalajemadera.shared.domain.Despachante / 3149599025 | DESP || java.lang.Long / 4227064769 | Treyes 8978 - CAPITAL FEDERAL |gonzalo@rsystem.com.ar | Spina Gonzalo | 45510141 | direccion destino | direccion exportador|java.util.Date/3385151746|chasis/|gov.senasa.embalajemadera.shared.domain.ImportadorExportador/918958990|gonzalo@gmail.com|46326066|gov.senasa.embalajemadera .shared.domain.DatoAduana / 2671264783 | NRODESPACHO | IC01 | gov.senasa.embalajemadera.shared.domain.LugarDeArribo / 3008903128 | NROMANIIMPO | gov.senasa。 embalajemadera.shared.domain.PuntoIngreso / 1183502123 | 717.3 | aduana origen | Terrestre camion | merca | gov.senasa.embalajemadera.shared.domain.Pais / 3238585366 |澳大利亚|阿富汗| nombre exportador | gov.senasa.embalajemadera.shared.domain .TransportePatente / 1923027028 | acoplado |底盘| 1 | 2 | 3 | 4 | 1 | 5 | 6 | 7 | 0 | 0 | 8 | 1 | 9 | 0 | 10 | 1 | 10 | 10 | 11 | 10 | 12 | 0 | 0 | 11 | 10 | 126951 | 0 | 0 | 0 | 0 | 0 | 13 | 1 | 14 | 0 | 0 | 15 | 16 | 1 | 17 | 0 | 18 | 10 | 19 | 20 | ZRCrAA | 21 | 22 | 19 | 0 | 0 | 23 | 24 | 0 | 0 | 0 | 0 | 0 | 25 | 26 | 0 | 0 | 27 | VnTkM $ A | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 28 | -11 | 29 | 0 | 0 | 19 | -13 | 21 | 30 | 0 | 0 | 0 | 23 | 31 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | 1 | 32 | 0 | 16 | 0 | -18 | 0 | 0 | -2 | 0 | 0 | 33 | 0 | 0 | 0 | 27 | VnTkM $ A | 0 | 34 | 35 | 0 | 0 | 0 | 10 | 24754701 | 0 | -18 | 36 | 19 | 37 | 10 | 38 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -20 | 39 | 40 | 41 | 0 | 0 | 0 | 42 | 0 | 43 | 10 | 10 | 42 | 10 | 44 | 10 | 1 | 0 | -22 | -5 | 45 | 0 | 0 | 0 | -18 | 8 | 1 | 46 | 0 | 0 | 0 | 19 | 47 |我已阅读 href =https://docs.google.com/document/d/1eG0YocsYYbNAtivkLtcaiEE5IOF5u4LUol8-LL0TIKU/edit =nofollow>这个,但正如你所看到的,这比上面的例子稍微复杂一点文档。我无法构建有效载荷。我需要一个兼容VB6或PHP的方法,或者只是一个很好的解释,所以我可以做我自己的例程。谢谢 解决方案

编辑:我回答了反序列化(如果你要对结果做些什么,来自服务器),但你要求序列化。第一部分仍然是对一个请求的反序列化,但是在中断之后,我将展示如何序列化一个请求,再次以我们在这个问题中的信息有限为例。

< hr>

请求反序列化



它可能比文档中的示例更大,但适用相同的规则。对于像这样的请求,我们按照以下方式进行分解,拆分 | 字符。


7


版本7.


0


未设置标志。


48


接下来的48个令牌是字符串表,他们。这些字符串将表示要传递的数据的类型,以及实际的Java字符串。



因此,我们将字符串读入String [48],然后将所有其余的数字是引用或基元。像 ZRCrAA VnTkM $ A 这样的数据可能是一个base64编码的long。把它当作一个字符串列表,当我们从数据开始请求对象时,我们会使用它。



正如文档所说,我们现在从第二个列表中,引用(以 1 | 2 | 3 | 4 | 1 | 5 | 6 | 7 | 0 | 0 | 8 ... 开头)。随着我们继续反序列化,我们还需要四件事:应用程序的URL,策略的强名称,我们将要调用的服务类以及该服务类中的方法名称。



由于这些都是字符串对象,我们将读取字符串。在 com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader 中,我们看到readString()看起来像这样: / p>

  @Override 
public String readString()throws SerializationException {
return getString(readInt());





$ b因此,首先我们读取一个int,然后使用它来查找一个字符串。这里是getString:

  @Override 
保护字符串getString(int index){
if(index == 0){
return null;
}
//索引是基于1的
断言(索引> 0);
assert(index< = stringTable.length);
return stringTable [index - 1];

$ / code>

现在,我们的第一部分数据是基本url,读为字符串。我们看到 1 作为我们读取的整数,然后我们得到上面创建的String [48]中的第(1 - 1)个字符串:


https:/ /aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/


强大的政策名称是next , 2 ,我们将其解读为

lockquote
03152A2DEBABDCE5D33BF4C88511DD1E

服务器将使用它并找到名为03152A2DEBABDCE5D33BF4C88511DD1E.gwt.rpc的文件,概述可在服务器上创建的内容的安全策略(以禁止黑客只是在服务器上创建任何类型的对象)。

接下来我们寻找服务类 3


net.customware.gwt.dispatch.client.standard.StandardDispatchService


最后调用方法 4


执行


从这里开始,你需要知道什么 StandardDispatchService.execute - 我们知道它只有一个参数,可能是 Action 实例,因为我们看到 1 表示一个参数,那么 5 ,如果解码为Object我们读取第5个字符串(查看readObject的工作原理以了解原因)。不知道 Action 或其他类具有哪些字段(下面列出),我们无法真正猜到接下来会发生什么情况:




  • net.customware.gwt.dispatch.shared.Action

  • gov.senasa.embalajemadera.shared.rpc.actions.IngresarDeclaracionJuradaAction

  • gov.senasa.embalajemadera.shared.domain.DeclaracionJurada

  • gov.senasa.embalajemadera.shared.domain.TipoEmbalajeCantidad

  • gov.senasa.embalajemadera.shared.domain.TipoEmbalaje

  • gov.senasa.embalajemadera.shared.domain.Contenedor

  • gov .senasa.embalajemadera.shared.domain.Despachante


    (请注意,我只是猜测这些是由它们的包装可序列化的类和名字 - 它们可能只是一些普通的字符串,有人决定通过电话发送它们作为请求的一部分!)






    请求序列化



    我们已经介绍过了试图分解这个消息的很多基础知识,所以让我们试着把它放在一起。管理这个的GWT客户端代码中的类是 com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter 。方法 prepareToWrite() toString()有助于设置舞台,显示我们的第一个也是最后一个事物。我们会在我们的工作中做的:

      / ** 
    *在尝试附加任何令牌之前调用此方法。此方法
    *实施< b>必须< / b>被任何重写的版本调用。
    * /
    @Override
    public void prepareToWrite(){
    super.prepareToWrite();
    encodeBuffer = new StringBuilder();

    //写入序列化策略info
    writeString(moduleBaseURL);
    writeString(serializationPolicyStrongName);
    }

    @Override
    public String toString(){
    StringBuilder buffer = new StringBuilder();
    writeHeader(buffer);
    writeStringTable(buffer);
    writePayload(buffer);
    return buffer.toString();



    $ b $ prepareToWrite()方法通过添加两个字符串(模块基本url和策略强名称)来启动流,您将从反序列化过程中识别出字符串。 toString()方法显示了我们将写出的三个阶段:标头,字符串表和有效载荷,或者对象引用和原始值。


    为什么我们的字符串跟其他叶子值不同?这样我们就可以将所有的字符串放在一个地方,这样我们就可以多次引用它们,并且每次只发送一次。与XML或JSON相对比,每当你想使用一个值时,即使它完全一样,你也必须重新编写值。



    标题包含(最新版本是7),以及要设置的标志(在你的示例中,只有0会做)

    在超类 AbstractSerializationStreamWriter ,有四个字段:

      private int objectCount; 
    私人地图<对象,整数> objectMap = new IdentityHashMap< Object,Integer>();
    私人地图< String,Integer> stringMap = new HashMap< String,Integer>();
    私人列表< String> stringTable = new ArrayList< String>();

    第一个是每个对象的当前索引 - 我们将使用它来跟踪对象以前见过。接下来 objectMap ,以便我们可以检查每个对象并检查是否曾经见过它,如果是,在哪里,以便我们可以将引用写回该位置。 stringMap 字段的功能相同,但是对于字符串 - JS专门处理字符串键。最后, stringTable 本身,即我们所见过的所有字符串的列表,每个只添加一次。



    如果您将服务方法的已编译应用程序生成的Java拆分为 List< String> filterStrings(List< String> strings,String startsWith),你会看到类似这样的内容:

    > ClientSerializationStreamWriter streamWriter = ...; //用序列化器创建
    streamWriter.prepareToWrite();
    streamWriter.writeString(com.acme.project.shared.MyService); //服务接口
    streamWriter.writeString(filterStrings); //方法名称
    streamWriter.writeInt( 2); //在流中找到的参数的数量
    streamWriter.writeObject(strings);
    streamWriter.writeString(startsWith);

    确切知道每种方法会写些什么取决于知道Java中的方法签名是什么 - 刚刚编译的GWT JS和一个有效载荷示例,逆向工程有点困难。但让我们继续看看接下来会发生什么。

    writeObject 的实现需要该对象并首先记录它类型。如果该对象为空,那么我们只写一个空字符串(a.k.a. 0 )并完成。否则,我们检查是否我们之前已经写过这个对象(并且写入一个负数来查看负载的去向),或者我们需要查看如何编写该对象的其余部分,并序列化每个字段。 p>

    每个可以序列化的对象都必须有一个FieldSerializer,它描述了如何对该对象进行编码和解码。 GWT中有许多CustomFieldSerializer,为特定目的定制实现,它告诉RPC不会自动生成序列化程序。一个例子可能是ArrayList,如果我们已经通过 - ArrayList_CustomFieldSerializer 委托给 Collection_CustomFieldSerializerBase ,它会这样做:

      public static void serialize(SerializationStreamWriter streamWriter,
    Collection instance)throws SerializationException {
    int size = instance。尺寸();
    streamWriter.writeInt(size);
    for(Object obj:instance){
    streamWriter.writeObject(obj);


    首先我们写出列表的大小,这样解串器知道要读取多少个元素,然后我们在列表中写入每个项目。在我们的例子中,我们将这些全部写成字符串。然后,我们将写一个 字符串,这是方法的第二个参数。

    所以,我们在String表中有这样的数据:


    • baseUrl

    • 政策字符串名称

    • com .acme.project.shared.MyService

    • filterStrings
    • java.util.ArrayList

    • java.lang.String

    • 参数 strings 中的每个字符串,以及字符串 startsWith ,但由于我们不知道是否有重复,我们无法知道是否会有相同数量的字符串。



    在我们的有效载荷中,可以说我们称之为 filterStrings([a,ab,abc,a],ab code>,我们将引用1(baseurl字符串索引),2(策略强名字字符串索引),3(服务名字符串索引),4(方法名字符串索引),2(int字段数(期望值),5(字符串列表参数的类名称),4(列表中的项目数),6 (列表中第一项的字符串),7(a)的内容,6(字符串类型),8(ab的内容),6(字符串类型),9(abc的内容) ,6(字符串类型),7(a的内容),6(第二个参数的字符串类型)和最后8(再次的ab的内容)。

    这将如何寻找像Action,DeclaracionJurada等类?不知道那里有什么领域和他们发生什么顺序,我们不能肯定地说。从有效负载重构内容没有什么好方法,但是如果您可以在发送有效负载之前调试正在运行的应用程序,则可以观察要序列化的对象的结构,并使用它来确定您所拥有的内容在流中找到。我观察到样本流中有几个负数,表明负值对于用例很重要,或者存在反向引用,并且这不是简单的对象树,而是完整的图,它会使事情稍微复杂些。




    RPC序列化格式并不复杂 - 我强烈建议您阅读各种 com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader 子类来了解它的功能。从那里,你应该能够将这两个值列表(字符串和引用/基元)解析成实际的对象,加上所有可能通过线路发送的类的结构,并用任何语言或框架重新实现。


    I'm wondering if there's a method to get a serialized string from a "string table".

    What i need, is to POST some data in a web page form. This is what it sends when i do it manually:

    7|0|48|https://aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/|03152A2DEBABDCE5D33BF4C88511DD1E|net.customware.gwt.dispatch.client.standard.StandardDispatchService|execute|net.customware.gwt.dispatch.shared.Action|gov.senasa.embalajemadera.shared.rpc.actions.IngresarDeclaracionJuradaAction/2514804035|gov.senasa.embalajemadera.shared.domain.DeclaracionJurada/1628723960|java.util.ArrayList/4159755760|gov.senasa.embalajemadera.shared.domain.TipoEmbalajeCantidad/4152068152|java.lang.Integer/3438268394|Pallet|gov.senasa.embalajemadera.shared.domain.TipoEmbalaje/309031988|java.util.HashSet/3273092938|gov.senasa.embalajemadera.shared.domain.Contenedor/1178264080|nro contenedor 1|java.lang.Boolean/476441737|gov.senasa.embalajemadera.shared.domain.Despachante/3149599025|DESP||java.lang.Long/4227064769|Treyes 8978 - CAPITAL FEDERAL|gonzalo@rsystem.com.ar|Spina Gonzalo|45510141|direccion destino|direccion exportador|java.util.Date/3385151746|chasis/|gov.senasa.embalajemadera.shared.domain.ImportadorExportador/918958990|gonzalo@gmail.com|46326066|gov.senasa.embalajemadera.shared.domain.DatoAduana/2671264783|NRODESPACHO|IC01|gov.senasa.embalajemadera.shared.domain.LugarDeArribo/3008903128|NROMANIIMPO|gov.senasa.embalajemadera.shared.domain.PuntoIngreso/1183502123|717.3|aduana origen|Terrestre camion|merca|gov.senasa.embalajemadera.shared.domain.Pais/3238585366|AUSTRALIA|AFGHANISTAN|nombre exportador|gov.senasa.embalajemadera.shared.domain.TransportePatente/1923027028|acoplado|chasis|1|2|3|4|1|5|6|7|0|0|8|1|9|0|10|1|10|0|11|0|12|0|0|11|10|126951|0|0|0|0|0|13|1|14|0|0|15|16|1|17|0|18|0|19|20|ZRCrAA|21|22|19|0|0|23|24|0|0|0|0|0|25|26|0|0|27|VnTkM$A|0|0|0|0|0|0|0|28|-11|29|0|0|19|-13|21|30|0|0|0|23|31|0|0|0|0|0|0|8|1|32|0|16|0|-18|0|0|-2|0|0|33|0|0|0|27|VnTkM$A|0|34|35|0|0|0|10|24754701|0|-18|36|19|37|0|38|0|0|0|0|0|0|0|-20|39|40|41|0|0|0|42|0|43|10|10|42|0|44|10|1|0|-22|-5|45|0|0|0|-18|8|1|46|0|0|0|19|47|48|0|0|0|0|0|0|0|0|0|

    I've already read this but as you see, this is a little more complex than the example in the documentation. I am not being able to build the payload. I need a method compatible with VB6 or PHP, or just a good explanation so i can make my own routine. Thanks

    解决方案

    Edit: I answered deserialization (which you may still need if you are going to do something with the results from the server), but you asked for serialization. The first part is still deserialization of a request, but below the break, I'll show how to serialize a request, again with as limited of information as we have in this question


    Request Deserialization

    It may be bigger than the example in the document, but the same rules apply. For a request like this one, we break it down as follows, splitting on the | character.

    7

    Version 7.

    0

    No flags are set.

    48

    The next 48 tokens are the string table, build an array out of them. Those strings will represent the types of the data being passed, as well as actual Java Strings.

    So we read the strings into a String[48], and then all the remaining numbers are either references or primitives. Data like ZRCrAA and VnTkM$A is probably a base64 encoded long. Treat this as also a list of strings, and we'll use this when we start requesting object from the data.

    As the document says, we now read from the second list, the references (starts with 1|2|3|4|1|5|6|7|0|0|8...). As we continue to deserialize, we need four more things: the url of the app, the strong name of the policy, the service class we are about to invoke, and the method name in that service class.

    Since these are all string objects, we'll read strings. In com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader, the actual Java class which would read this, we see that readString() looks like this:

    @Override
    public String readString() throws SerializationException {
      return getString(readInt());
    }
    

    So first we read an int, then use that to find a string. Here's getString:

    @Override
    protected String getString(int index) {
      if (index == 0) {
        return null;
      }
      // index is 1-based
      assert (index > 0);
      assert (index <= stringTable.length);
      return stringTable[index - 1];
    }
    

    Now, our first piece of data was the base url, read as a string. We see 1 as the int we read, then we get the (1 - 1)th string in the String[48] we created above:

    https://aps2.senasa.gov.ar/embalaje-madera-web/embalajeApp/

    The strong policy name is next, 2, which we read as

    03152A2DEBABDCE5D33BF4C88511DD1E

    The server will use this and find a file named 03152A2DEBABDCE5D33BF4C88511DD1E.gwt.rpc, outlining a security policy of what can be created on the server (so as to prohibit a hacker from just creating any kind of objects on your server).

    Next we look for the service class, 3:

    net.customware.gwt.dispatch.client.standard.StandardDispatchService

    And finally the method to invoke, 4:

    execute

    From here, you need to know what StandardDispatchService.execute - we know that it takes only one argument, probably an Action instance, since we see 1 indicating one argument, then 5, which if decoded as an Object means that we read the 5th string (take a look at how readObject works to see why). Without knowing what fields Action or the other classes have (listed below), we can't really guess as to what happens next with any certainty:

    • net.customware.gwt.dispatch.shared.Action
    • gov.senasa.embalajemadera.shared.rpc.actions.IngresarDeclaracionJuradaAction
    • gov.senasa.embalajemadera.shared.domain.DeclaracionJurada
    • gov.senasa.embalajemadera.shared.domain.TipoEmbalajeCantidad
    • gov.senasa.embalajemadera.shared.domain.TipoEmbalaje
    • gov.senasa.embalajemadera.shared.domain.Contenedor
    • gov.senasa.embalajemadera.shared.domain.Despachante

    (please note that I'm only guessing that these are serializable classes by their package and name - they could be just plain strings that someone decided to send over the wire as part of their request!)


    Request Serialization

    We've already covered a lot of the basics in trying to take apart this message, so lets try to put it together. The class in the GWT client code that manages this is com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter. The methods prepareToWrite() and toString() help set the stage a bit, showing the very first and last things we'll do around our work:

    /**
     * Call this method before attempting to append any tokens. This method
     * implementation <b>must</b> be called by any overridden version.
     */
    @Override
    public void prepareToWrite() {
      super.prepareToWrite();
      encodeBuffer = new StringBuilder();
    
      // Write serialization policy info
      writeString(moduleBaseURL);
      writeString(serializationPolicyStrongName);
    }
    
    @Override
    public String toString() {
      StringBuilder buffer = new StringBuilder();
      writeHeader(buffer);
      writeStringTable(buffer);
      writePayload(buffer);
      return buffer.toString();
    }
    

    The prepareToWrite() method starts off the stream with adding two strings - the module base url, and the policy strong name, strings you will recognize from the deserialization process. The toString() method shows the three stages that we will write out: The header, the string table, and the "payload", or object references and primitive values.

    Why do we have Strings tracked differently from other leaf values? This way we have all the strings in one places, so that we can refer to them more than once and only send them each a single time. Contrast this with XML or JSON where every time you want to use a value, you have to write the value again, even if it is exactly the same.

    The header consists of the version (latest is 7), and the flags to set (in your sample, just 0 will do).

    In the superclass AbstractSerializationStreamWriter, there are four fields:

    private int objectCount;
    private Map<Object, Integer> objectMap = new IdentityHashMap<Object, Integer>();
    private Map<String, Integer> stringMap = new HashMap<String, Integer>();
    private List<String> stringTable = new ArrayList<String>();
    

    The first is the current index of each object - we'll use that to keep track of objects we've seen before. Next objectMap, so that we can examine each object and check if we've seen it before, and if so, where, so that we can write a reference back to that position. The stringMap field does the same thing but for strings - JS treats string keys specially. Finally, the stringTable itself, the list of all strings we've seen, each added only once.

    If you take apart a compiled application's generated Java for a service method like List<String> filterStrings(List<String> strings, String startsWith), you'll see something like this:

    ClientSerializationStreamWriter streamWriter = ...;//create with serializer
    streamWriter.prepareToWrite();
    streamWriter.writeString("com.acme.project.shared.MyService");//service interface
    streamWriter.writeString("filterStrings");//method name
    streamWriter.writeInt(2);//number of arguments to be found in the stream
    streamWriter.writeObject(strings);
    streamWriter.writeString(startsWith);
    

    Knowing exactly what will be written for each method depends on knowing what the method signature is in Java - with just compiled GWT JS and a payload sample, it is somewhat difficult to reverse engineer. But lets carry on and see what would happen next.

    The implementation of writeObject takes the object and first notes its type. If the object is null, then we just write a null string (a.k.a. 0) and are done. Otherwise we check if we have already written this object before (and so write a negative number to see where to go in the payload), or we need to look up how to write the rest of that object, and serialize each field.

    Each object that can be serialized must have a FieldSerializer, which describes how to encode and decode that object. There are many CustomFieldSerializers in GWT, custom implementations for a specific purpose, which tell RPC to not automatically generate a serializer. One example might be for ArrayList, if we had passed that in - ArrayList_CustomFieldSerializer delegates to Collection_CustomFieldSerializerBase, which does this:

    public static void serialize(SerializationStreamWriter streamWriter,
        Collection instance) throws SerializationException {
      int size = instance.size();
      streamWriter.writeInt(size);
      for (Object obj : instance) {
        streamWriter.writeObject(obj);
      }
    }
    

    First we write the size of the list, so that the deserializer knows how many elements to read, and then we write each item in the list. In our case, we'll write these all as strings. Then, we'll write one more string, the second argument to the method.

    So, we have this data in our String table:

    • baseUrl
    • policy string name
    • "com.acme.project.shared.MyService"
    • "filterStrings"
    • "java.util.ArrayList"
    • "java.lang.String"
    • each string in the argument strings, and the string startsWith, though since we don't know if there are duplicates, we can't know if there are going to be the same number of strings.

    In our payload then lets say we called filterStrings(["a", "ab", "abc", "a"], "ab"), we'll have references 1 (baseurl string index), 2 (policy strong name string index), 3 (service name string index), 4 (method name string index), 2 (int for number of fields to expect), 5 (class name of the strings list argument), 4 (number of items in the list), 6 (type of the first item in the list, string), 7 (contents of "a"), 6 (string type), 8 (contents of "ab"), 6 (string type), 9 (contents of "abc"), 6 (string type), 7 (contents of "a"), 6 (string type of second argument), and finally 8 (contents of "ab" again).

    How would this look for classes like Action, DeclaracionJurada, etc? Without knowing what fields there are and what order they happen in, we can't say for sure. There is no good way just from the payload to reconstruct the contents, though if you could debug the running app just before the payload is sent, you could observe the structure of the object to be serialized, and use that to decide what you've found in the stream. I observe that there are several negative numbers in the sample stream, suggesting either that negative values are important for the use case, or there are back references and that this isn't a simple object tree, but a full graph, which will make things slightly more complicated.


    The RPC serialization format is not complicated - I highly encourage reading the code in the various com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader subclasses to understand what it does. From there, you should be able to parse out these two lists of values (strings and references/primitives) into actual objects, plus the structure of all of the classes that might be sent over the wire, and reimplement it in any language or framework.

    这篇关于序列化RPC-GWT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆