【问题标题】:Deserializing json string to fluid objects with genson使用genson将json字符串反序列化为流体对象
【发布时间】:2014-02-22 16:22:10
【问题描述】:

我正在尝试将 json 文档中的简单字符串元素数组转换为 java 列表,该列表位于使用 genson 的 bean 内。我期待这会自动发生,但它不起作用。我错过了什么吗?

这是我的 Json 字符串:

{"id":12345,"permissions":["READ_XX","WRITE_XX"]}

这是我的对象的输出,表明我的权限列表没有被填充:

DefaultRole [id=12345, name=null, permissions=[]]

我的 DefaultRole 类如下所示:

public class DefaultRole implements Role
{
  public Id id = null;

  @XmlElementWrapper(name = "permissions")
  @XmlElement(name = "permission")
  private List<String> permissions = Lists.newArrayList();

  @Inject
  public DefaultRole()
  {

  }
  [...]

JAXB 注释仅用于 XML 转换。他们不应该在这里发挥作用。

我正在创建这样的 Genson 对象:

Genson genson = new Genson.Builder()
  .setWithClassMetadata(false)
  .setWithBeanViewConverter(true)
  .setWithDebugInfoPropertyNameResolver(true)
  .setSkipNull(true)
  .create();

我的反序列化调用:

genson().deserialize(jsonString, DefaultRole.class)

谁有关于如何将我的 json 字符串数组反序列化为驻留在 bean 内的 java List 对象的提示?

编辑 [部分解决 - 仍有一个问题未解决]

我现在更进一步了。我回到根源,从一个简单的包含字符串列表的基本 bean 开始。正如预期的那样,这是第一次。与上一个对象的区别主要是这个遵循bean规范。另一个对象(DefaultRole)遵循流体对象模式(不确定这是否是正确的定义)。基本上,我不是将 setter 方法设为 void,而是返回对象,以便可以轻松地以流畅的方式设置下一个值。

所以这行得通:

public void setPermissions(List<String> permissions)
{
  this.permissions = permissions;
}

这不起作用:

public Role setPermissions(List<String> permissions)
{
  this.permissions = permissions;
  return this;
}

有其他人遇到过这种情况吗?有什么替代方法可以将我的所有 bean 切换为遵守 bean 规范?是使用纯字段级填充而不是通过 setter 方法的唯一选择吗?

编辑[几乎解决]

您好,我不确定如何最好地回答需要代码来帮助理解我所做的问题。

无论如何,非常感谢您的帮助。我真的很喜欢第三个选项,这是我一直在寻找的类型。不幸的是,我现在收到错误“没有找到类型类 xxx.DefaultRole 的构造函数”。根据您在回答中所说的,当搜索继续时返回 Trilean.UNKNOWN 时不应发生这种情况。

我将以下代码添加到我的 genson-builder 中:

        .set(new BeanMutatorAccessorResolver.BaseResolver()
        {
            @Override
            public Trilean isMutator(Method method, Class<?> fromClass)
            {
                if (Reflect.isSetter(method))
                    return Trilean.TRUE;

                else
                    return Trilean.UNKNOWN;
            }
        })

我的 Reflect.isSetter(method) 看起来像这样(代码改编自这里:http://www.asgteach.com/blog/?p=559):

public static boolean isSetter(Method method)
{
    return Modifier.isPublic(method.getModifiers()) &&
        (method.getReturnType().equals(void.class) || method.getReturnType().equals(method.getDeclaringClass())) &&
        method.getParameterTypes().length == 1 &&
        method.getName().matches("^set[A-Z].*");
}

BaseResolver 返回 Trilean.UNKNOWN 用于尚未实现的所有内容。因此它应该使用标准逻辑找到构造函数,不是吗?

编辑[已解决]

为了完整起见,我将发布实际有效的代码:

public static final Genson genson()
{
    Genson genson = new Genson.Builder()
        .setSkipNull(true)
        .setWithClassMetadata(false)
        .setWithDebugInfoPropertyNameResolver(true)
        .setWithBeanViewConverter(true)
        .with(new BeanMutatorAccessorResolver.BaseResolver()
        {
            @Override
            public Trilean isMutator(Method method, Class<?> fromClass)
            {
                if (Reflect.isSetter(method))
                    return Trilean.TRUE;

                else
                    return Trilean.UNKNOWN;
            }
        })
        .create();

    return genson;
}

这里需要注意的是,“.set(new BeanMutatorAccessorResolver.BaseResolver()”必须替换为“.with(new BeanMutatorAccessorResolver.BaseResolver()”(注意“with”而不是“set”)。这一点很重要,因为不再使用标准解析器,否则您最终会遇到我遇到的错误,即无法再找到构造函数。

isSetter 方法如下所示:

public static boolean isSetter(Method method)
{
    return Modifier.isPublic(method.getModifiers())
        && (method.getReturnType().equals(void.class) || method.getReturnType().isAssignableFrom(method.getDeclaringClass()))
        && method.getParameterTypes().length == 1
        && method.getName().matches("^set[A-Z].*");
}

这里需要注意的是,在将返回类型与声明类进行比较时,我最初使用的是“equals”而不是“isAssignableFrom”。这只适用于返回类型与声明它的类完全相同的类。当使用接口作为返回值时,它不再起作用。通过使用“method.getReturnType().isAssignableFrom(method.getDeclaringClass())”,这也适用于接口(包括超级接口)。

谢谢, 迈克尔

【问题讨论】:

  • 确实应该,你用的是什么版本? (顺便说一句,您应该添加评论或其他内容,以便人们收到您有其他问题等的通知)
  • 您也可以在 gensons 邮件列表上发布消息,并附上重现您问题的最小示例
  • 谢谢,我用的是 0.98。
  • 好的,下次会做,谢谢。
  • 嗯,我无法重现您的问题。这应该有效。您能否在 ML 上发布一个问题,并附上一个完整的示例来重现此行为(带有 genson setup + DefaultRole 定义的 main,所以我只需运行它即可查看会发生什么)?

标签: java json serialization fluent-interface genson


【解决方案1】:

确实,Genson(与大多数其他库一样)使用 Java bean 约定来检测 setter/getter,因此它不会将您的 setPermissions 检测为 setter。

两个快速解决方案:

1) 您可以使用@JsonProperty 对其进行注释(无需定义名称)。这将告诉 genson 将其用作 setter (但是 genson 不会重用返回的对象,这意味着在您的情况下它可以工作,但是像这样的 builder 是 nut 支持的)。

2)您可以强制genson只使用字段而不使用方法(这直接设置字段值而不调用get/set)。

Genson.Builder()
        .setUseFields(true)
        .setFieldFilter(VisibilityFilter.DEFAULT)
        .setUseGettersAndSetters(false)
      .create();

更通用:

3) 在链中添加一个自定义 BeanMutatorAccessorResolver,它将处理您的特定情况

Genson genson = Genson.Builder().with(new BeanMutatorAccessorResolver.BaseResolver() {
        @Override
        public Trilean isMutator(Method method, Class<?> fromClass) {
            // if method starts with setXXX and return type same as method.getDeclaringClass
            return Trilean.TRUE;
            // else return Tilean.UNKNOWN genson will use its built-in resolvers
        }
    }).create();

编辑

应该看到您在构建器上使用 set 而不是 with 方法。当您在 genson API 中看到 setXXX 时,这通常意味着您将覆盖/强制某些机制(您设置一个值),但 with 用于添加行为。

【讨论】:

  • 顺便说一句,因为 0.95 版 genson 有 partial support for JAXB annotations,所以你甚至可以使用 json 和 xml ser/deser 之间的类似行为,只需在之前进行一些测试以确保它是对称的
  • 您好,我在最初的问题中已经回答了您的问题。我已经看到在 genson 中有 JAXB 支持,但我试图尽可能地采用 genson 方式(并尽可能少地使用 JAXB 注释)。我将它与 jersey 结合使用,我首选的方法是让 jersey 与 genson 很好地工作,而不是让 jersey 与 JAXB 一起工作。我对 genson 真的很满意——我只需要了解一两件事就可以让它变得完美。
  • 是的,你是绝对正确的,我看到了“with”方法并且想知道......有时最好只是尝试一下。 “setXX”听起来好像我正在用我的设置(并覆盖)原始值。无论如何它现在可以工作了,非常感谢您的时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-04
  • 2020-02-03
  • 1970-01-01
  • 2012-04-24
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
相关资源
最近更新 更多