考虑一个不可变类 Foo(一个由 ID 和名称组成的 POJO),它需要序列化以便将数据从服务器发送到客户端。
public final class Foo
{
private final int m_id;
private final String m_displayName;
private Foo(final int id, final String displayName)
{
m_id = id;
m_displayName = displayName;
}
public static Foo create(final int id, final String displayName)
{
// Some error checking occurs here.
. . .
m_id = id;
m_displayName = displayName;
}
// Getters etc.
. . .
}
Foo 对象的实例化是通过静态工厂函数进行的,并且由于该对象是不可变的,因此没有零参数构造函数。
同时考虑一个不可变类 Bar,它包含一个数据成员 Foo 并为其实例化实现 Builder 模式(从片段中省略,因为它与问题无关)。
public final class Bar
{
private final Foo m_foo;
. . .
private Bar(final Builder builder)
{
. . .
}
public static Builder createBuilder()
{
return new Builder();
}
}
在评估了我应该序列化该对象的方式的选择之后,在不删除其不可变性或为序列化添加任何零参数构造函数的情况下,我得出的结论是,我需要实现一个 CustomFieldSerializer(对于客户端和服务器)。
我遵循了 Server Communication 中的指导方针GWT 的文章并实现了我自己的 CustomFieldSerializer,如下所示。
// Contains the serialization logic of the class Bar.
public final class Bar_CustomFieldSerializerBase
{
public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException
{
return Bar.createBuilder().forFoo((Foo) streamReader.readObject()).build();
}
public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance)
throws SerializationException
{
// . . .
streamWriter.writeObject(instance.getFoo());
}
public static void deserialize(final SerializationStreamReader streamReader, final Bar instance)
{
/*
* Empty as everything is handled on instantiateInstance().
*/
}
}
// The CustomFieldSerializer for class Bar.
public class Bar_CustomFieldSerializer extends CustomFieldSerializer<Bar>
{
public static void deserialize(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException
{
Bar_CustomFieldSerializerBase.deserialize(streamReader, instance);
}
public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException
{
Bar_CustomFieldSerializerBase.serialize(streamWriter, instance);
}
public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException
{
return Bar_CustomFieldSerializerBase.instantiate(streamReader);
}
@Override
public boolean hasCustomInstantiateInstance()
{
return true;
}
@Override
public Bar instantiateInstance(final SerializationStreamReader streamReader) throws SerializationException
{
return instantiate(streamReader);
}
@Override
public void deserializeInstance(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException
{
deserialize(streamReader, instance);
}
@Override
public void serializeInstance(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException
{
serialize(streamWriter, instance);
}
// Server side CustomFieldSerializer for class Bar.
public class Bar_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<Bar>
{
public static void deserialize(ServerSerializationStreamReader streamReader, Bar instance,
Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException
{
/*
* Empty as everything is handled on instantiateInstance().
*/
}
@Override
public Bar instantiateInstance(ServerSerializationStreamReader streamReader) throws SerializationException
{
return Bar_CustomFieldSerializerBase.instantiate(streamReader);
}
@Override
public void deserializeInstance(ServerSerializationStreamReader streamReader, Bar instance,
Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException
{
deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes);
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, Bar instance) throws SerializationException
{
Bar_CustomFieldSerializerBase.deserialize(streamReader, instance);
}
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, Bar instance) throws SerializationException
{
Bar_CustomFieldSerializerBase.serialize(streamWriter, instance);
}
}
由于类 bar 包含一个需要序列化的 Foo 对象,我继续并实现了另一组 CustomFieldSerializers,这次对于类 Foo,客户端和服务器都遵循相同的模式。
问题出现在类 Bar 发生序列化时,特别是此时:
public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance)
throws SerializationException
{
// . . .
streamWriter.writeObject(instance.getFoo());
}
我收到的异常消息如下:
[WARN] Exception while dispatching incoming RPC call com.google.gwt.user.client.rpc.SerializationException: Type 'ui.shared.models.fooItems.Foo' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
writeObject() 似乎无法序列化 Foo 类型的对象,因为 Foo 类不属于白名单项目,即使已为客户端和服务器提供了自定义序列化程序。
我总是可以跳过 writeObject() 调用并为每个 Foo 的数据成员调用 writeInt() & writeString()(这是工作良好),但我更愿意让 writeObject() 工作。我提出的解决方案极容易出现维护错误,因为将来 Foo 类中的任何更改都必须反射(reflect)在 Foo 的序列化程序(显而易见)和 Bar 的序列化程序(不太明显)上。
我已经尝试了几乎所有我能在网上找到的东西,从在 Foo 和 Bar 上实现 isSerializable 接口(interface)(没有任何区别,自 AFAIK 以来它不应该有任何区别提供自己的自定义序列化程序的类不需要遵守此规则)甚至提供私有(private)的零参数构造函数(这也不应该有任何区别,因为自定义字段序列化程序的实例化函数应该通过静态工厂)。
为什么 Foo 类没有被列入白名单?我是不是漏掉了一些明显的东西或者误解了什么?
提前感谢您抽出时间。
最佳答案
这里的问题是,您很可能从未在代码中的任何地方明确提及 Foo 类曾被发送到服务器。例如。你只有这样的服务方法:
interface MyService {
Foo getFoo(Bar bar);
}
要使用writeObject,您需要明确提示GWT,将Foo 类包含到反序列化列表中,方法是添加一个新的服务方法,该方法采用 Foo 类作为参数:
interface MyService {
Foo getFoo(Bar bar);
void setFoo(Foo foo);// letting GWT know that we might send Foo object over the wire, you don't have to ever call this method in your app, or implement it in some meaningful way, just let it be there
}
您不必在您的应用中调用此方法,也不必为其提供任何实现。但否则它不会工作。很少有其他方法可以为 GWT 创建此提示,但想法是一样的,您将为 GWT-RPC 显式公开 Foo 类。还请记住,如果您在多个服务中使用 Bar 类,则必须将此类方法添加到使用 Bar 类的每个服务
现在,详细说明为什么会发生这种情况。在服务器端,GWT-RPC 跟踪两个列表:它可以序列化的对象和它可以反序列化的对象。此信息取自 RPC 策略 list 。在我的第一个示例中,我只提到了 Bar 对象作为可以发送到服务器的东西。但是由于您在 Bar 类上定义了一个自定义字段序列化程序,GWT 不会对 Bar 执行任何分析,因此它不知道 Foo<> 可能会被发送到服务器,因此它决定服务器端的 Foo 不需要反序列化器。因此,当您尝试通过网络发送 Bar 的实例时,服务器会尝试反序列化它,但由于 readObject 在自定义序列化程序中使用,它会尝试为 Foo 找到反序列化程序也一样,但这是不允许的,因此整个 deserealization 过程失败。如果您添加额外的服务方法,该方法仅通过网络发送 Foo 对象,GWT 会意识到此类对象也可以发送到服务器,因此它将 Foo 标记为也可以反序列化。
这非常令人困惑,我个人认为具有自定义序列化的类应该自动添加到所有白名单中,但事实就是如此。
编辑
另一个(没有愚蠢的空方法的非 hacky 解决方案)将使用专门的 DTO 层进行通信(例如,仅具有默认公共(public)构造函数的 POJO),但在您需要发送具有很多交叉引用。
关于java - GWT 自定义字段序列化问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15070383/
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>