【问题标题】:Java List of custom objects inside response object not populated with GSON unmarshaller未使用 GSON 解组器填充的响应对象内的自定义对象的 Java 列表
【发布时间】:2013-11-06 16:19:39
【问题描述】:

基本上,我在一台服务器上将其编组为 JSON,然后将其发送到另一台服务器,在那里它应该被解组。我使用一个称为列表包装器的响应对象,这样如果有任何错误,我可以将它们传递出去。通过 badgerfish(jettison) 在下面将 JSON 编组在一个 resteasy 类中,然后返回到另一个服务器,GSON 将解组为一个 listwrapper 对象,但里面的列表是空的。有任何想法吗? 注意:列表必须保持通用,因为不同的对象可能会进入列表,尽管列表一次总是只有一种类型。

json

解组

        GsonBuilder builder = new GsonBuilder();    
        Gson gson = builder.create(); 
        Object List;
        if (!JSON.equals("")) {
            List = gson.fromJson(new BufferedReader(new StringReader(JSON)), ListWrapper.class);
        }

列表包装器类型

@XmlRootElement(name = "ListWrapper")

public class ListWrapper {

    private Vector<Object> objects;
    private String status;
    private int batch;

    private ValidationException e;

    public ListWrapper() {
        this.setStatus("Success");
    }

    public ListWrapper(Vector<Object> list) {
        this.setStatus("Success");
        this.objects = list;
    }

    public ListWrapper(int x) {
        this.setStatus("batch");
        this.batch = x;
    }

    public Vector<Object> getList() {
        return objects;
    }

    public void setList(Vector<Object> object) {
        this.objects = object;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    @XmlJavaTypeAdapter(ThrowableAdapter.class)
    public ValidationException getE() {
        if (e != null) {
            return e;
        } else {
            return null;
        }
    }

    public void setE(ValidationException x) {
        this.e = x;
    }

    public int getBatch() {
        return batch;
    }

    public void setBatch(int batch) {
        this.batch = batch;
    }
}

【问题讨论】:

    标签: java json gson


    【解决方案1】:

    你不能直接使用ListWrapper反序列化,你需要一个容器类,这就是为什么你的列表是空的。顺便说一句,您的列表不是列表而是地图(大括号限制 list 的内容)。

    我创建了一个代码,可以正确解析您的 JSON,并为您提供一些简单的功能来提取数据。由于您希望保持在服务器之间传递的数据的灵活性,因此您需要字符串来访问数据。

    这是准备复制和粘贴的代码,您可以自己尝试一下。请记住,访问器方法基于您在示例中显示的结构。我为您提供了您可以从中提取的主要 4 种不同类型的数据。如果您需要更多相关信息,请告诉我。

    package stackoverflow.questions.q19817221;
    
    import java.util.*;
    
    import com.google.gson.Gson;
    
    public class Q19817221 {
    
        public class ListWrapper {
    
            private Map list;
            private Map status;
            private Map batch;
    
            private Object extractValue(Map m) {
                return m.get("$");
            }
    
            public Integer getBatch() {
                return Integer.valueOf( (String) extractValue(batch));
            }
    
            public Object getValueFromList(String key) {
                try {
                    Map m = (Map) list.get(key);
                    if (m != null)
                        return extractValue(m);
                } catch (Exception e) {
                    return list.get(key);
                }
                return null;
            }
    
            public Object getValueFromList(String secondLevelKey, String key) {
    
                Map secondLevelMap = (Map) list.get(secondLevelKey);
                try {
                    Map m = (Map) secondLevelMap.get(key);
                    if (m != null)
                        return extractValue(m);
                } catch (Exception e) {
                    return list.get(key);
                }
                return null;
    
            }
        }
    
        public class Container {
            public ListWrapper ListWrapper;
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
           String json = "{\"ListWrapper\":{\"batch\":{\"$\":\"0\"},\"list\":{\"@xmlns\":{\"xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},\"@xsi:type\":\"fidsUsers\",\"createdBy\":{\"$\":\"administrator\"},\"createdWhen\":{\"$\":\"2013-02-25T17:29:19-05:00\"},\"endDate\":{\"$\":\"2016-10-28T00:00:00-04:00\"}," +
                "\"isDisabled\":{\"$\":\"N\"},\"previousPasswords\":{\"$\":\"HXQDa4WxTdBmZtvhMVTgnw==@@@@zW6bdHkKdMN2p6CgRNjNHA==@@@@Sim7JN3kaHoXnh3KUS2++Q==@@@@Emz7zU0Wrm0lyb/K522O5A==@@ZirxzRl28JqfjOzIaMzAog==\"}," +
                "\"primaryKey\":{\"$\":\"David\"},\"pswdChgDate\":{\"$\":\"2013-07-12T08:27:46-04:00\"},\"pswdCount\":{\"$\":\"0\"},\"roleId\":{\"$\":\"Admin\"},\"roleIdFidsRoles\":{\"globalAccess\":{\"$\":\"Y\"},\"primaryKey\":{\"$\":\"Admin\"},\"roleDesc\":{\"$\":\"Administrator\"},\"roleId\":{\"$\":\"Admin\"}," +
                "\"updatedBy\":{\"$\":\"David\"}},\"startDate\":{\"$\":\"1992-07-28T00:00:00-04:00\"},\"updatedBy\":{\"$\":\"David\"},\"updatedWhen\":{\"$\":\"2013-10-02T10:46:31-04:00\"},\"userId\":{\"$\":\"David\"},\"userName\":{\"$\":\"David3\"},\"userPassword\":{\"$\":\"HXQDa4WxTdBmZtvhMVTgnw==\"}},\"status\":{\"$\":\"Success\"}}}";
    
    
           Container c = new Gson().fromJson(json, Container.class);
           ListWrapper lw = c.ListWrapper;
    
           System.out.println("batch:" + lw.getBatch());
           System.out.println("createdBy:" + lw.getValueFromList("createdBy"));
           System.out.println("@xsi:type: " + lw.getValueFromList("@xsi:type"));
    
           System.out.println("roleIdFidsRoles\\primaryKey: " + lw.getValueFromList("roleIdFidsRoles", "primaryKey"));
    
    
    
        }
    }
    

    这是执行结果:

    batch:0
    createdBy:administrator
    @xsi:type: fidsUsers
    roleIdFidsRoles\primaryKey: Admin
    

    【讨论】:

    • 但是如果我在列表中有不同的对象,现在很难使用字符串来找到它。你说我这个列表在序列化的时候其实已经是一张图了。我在 RestEasy 服务上使用 badgerfish(jettison) 从 JAXB xml 生成 json。你认为如果我只使用 GSON 创建 JSON,它会正确序列化为一个列表,以便我可以正确反序列化它吗?
    • 我真的不知道badgefish,所以我的评论将仅限于我所知道的:Gson。如果您在两台服务器上使用相同的技术(我的意思是 Java),您可以从一侧使用 Gson 进行序列化,然后在另一侧再次使用 Gson 进行反序列化。如果两台服务器都有一个包含你的类的 jar,那么做起来很简单。如果你是这种情况,我建议你只使用 Gson。关于这个问题,Gson 会生成一个地图,因为地图是在 Json 字符串中序列化的内容。例如,将您的 JSON 复制并粘贴到 json.parser.online.fr 以“导航”它。
    • 关于您当前的问题,您询问了如何反序列化,据我所知,这是您拥有的最佳选择。当然不是一个容易处理的对象,一旦你得到它。所以,再次强调,如果您可以在服务器的两端编写代码,请使用 Gson 序列化和反序列化传递您的类。
    【解决方案2】:

    通过查看您的 ListWrapper 课程和您发布的 json - 您有一个不匹配的问题,这可能是它无法解组的原因。

    对于: {"ListWrapper":{"batch":{"$":"0"},"list":{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"fidsUsers","createdBy":{"$":"administrator"},"createdWhen":{"$":"2013-02-25T17:29:19-05:00"},"endDate":{"$":"2016-10-28T00:00:00-04:00"},"isDisabled":{"$":"N"},"previousPasswords":{"$":"HXQDa4WxTdBmZtvhMVTgnw==@@@@zW6bdHkKdMN2p6CgRNjNHA==@@@@Sim7JN3kaHoXnh3KUS2++Q==@@@@Emz7zU0Wrm0lyb\/K522O5A==@@ZirxzRl28JqfjOzIaMzAog=="},"primaryKey":{"$":"David"},"pswdChgDate":{"$":"2013-07-12T08:27:46-04:00"},"pswdCount":{"$":"0"},"roleId":{"$":"Admin"},"roleIdFidsRoles":{"globalAccess":{"$":"Y"},"primaryKey":{"$":"Admin"},"roleDesc":{"$":"Administrator"},"roleId":{"$":"Admin"},"updatedBy":{"$":"David"}},"startDate":{"$":"1992-07-28T00:00:00-04:00"},"updatedBy":{"$":"David"},"updatedWhen":{"$":"2013-10-02T10:46:31-04:00"},"userId":{"$":"David"},"userName":{"$":"David3"},"userPassword":{"$":"HXQDa4WxTdBmZtvhMVTgnw=="}},"status":{"$":"Success"}}} 对我来说是一个 object ListWrapper,它有 3 个字段,分别命名为:batch、list 和 status,其中 batch 是一个对象,其中一个字段是数字,list 是一个映射,status 是一个包含一个字符串的对象。

    我对 badgerfish 不熟悉,话虽如此,在您的 ListWrapper 中尝试将 private Vector&lt;Object&gt; objects 更改为 private Map&lt;String,Object&gt; objects(当然还要匹配 getter/setter),看看是否可行

    【讨论】:

    • 制作地图的问题在于它不是地图。我有没有钥匙传入的对象。为了创建地图,我需要为我在服务器之间发送到的每个对象提供键(字符串)。
    【解决方案3】:

    列表必须保持通用,因为不同的对象可能进入 列表,尽管列表中总是只有一种类型 时间。

    如果您在解组之前知道它是什么类型,那么只有通过使用TypeToken 才能工作

    示例代码:

    Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
    gson.toJson(foo, fooType);
    
    gson.fromJson(json, fooType);
    

    另外,定义会变成这样:

        public class ListWrapper {
    
            private Vector<T> objects;
            private String status;
            private int batch;
    }
    

    public class ListWrapper {
    
        private Vector<T extends someBaseObject> objects;
        private String status;
        private int batch;
    }
    

    取决于您决定如何实施。

    【讨论】:

    • 这对于嵌套在我正在解组的对象中的列表如何工作?我看到了类型标记,这似乎是我想要的,但我不知道如何在这种情况下做到这一点,因为列表在另一个对象内,所以如果我用列表的类型标记解组该外部对象不会工作。
    • 检查我的答案here。你可以提取出内部列表,然后使用 TypeTokens 来解析它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多