简体   繁体   English

GSON:自定义对象反序列化

[英]GSON : custom object deserialization

Ok so I edited the question because it wasn't clear enough. 好的,所以我编辑了这个问题因为它不够清楚。

Edit 2 : updated the JSON file. 编辑2 :更新了JSON文件。

I'm using GSON in an Android app, and I need to parse JSON files, that come from a server, and are a little too complexes. 我在Android应用程序中使用GSON,我需要解析来自服务器的JSON文件,并且有点过于复杂。 I don't want to have my object structure too heavy, so I would like to simplify the contents : so the structure of my object won't be the structure of the JSON file. 我不希望我的对象结构太重,所以我想简化内容: 所以我的对象的结构将不是JSON文件的结构。

For example, if in the JSON I have this : 例如,如果在JSON中我有这个:

{
    "object1":{
        "attribute1" : "test1",
        "attribute40" : "test40",
        "user":{
            "id":1,
            "name":"foo"
        }
        ,"example":{
            "total":10,
            "list":[
            {
                "tag":"tag1",
                "name":"object name 1",
                "pos":1
            },
            {
                "tag":"tag10",
                "name":"object name 10",
                "pos":10
            }
        ]
        }
    }
    "object2":{
        "attribute1":"test..."
    }
}

I don't want to keep in my current object structure, an object Example , that contains an ArrayList and an int "total". 我不想保留我当前的对象结构,一个对象Example ,它包含一个ArrayList和一个int “total”。 But I would like to keep only a simple String with the value "object name 1;object name 2;..." . 但我想只保留一个简单的String,其值为"object name 1;object name 2;..."

Moreover, I would like to store only the user Id, not the complete User, because I already have the complete user stored somewhere else, with an other server API call. 此外,我想只存储用户ID,而不是完整的用户,因为我已经将完整的用户存储在其他地方,并且还有其他服务器API调用。

So my class class would be something like : 所以我的班级类似于:

class Foo{
    int userId;
    String example; //"object name 1;object name 2;..."
    ...
}

So I suppose that we can achieve this with a custom deserializer, but I don't find how. 所以我想我们可以使用自定义反序列化器实现这一点,但我找不到如何。 I would like if possible to minimize the memory, so I don't think that having a full object example, and then use it to build my String example is a correct way. 我想尽可能减少内存,所以我不认为有一个完整的对象示例,然后使用它来构建我的String example是一种正确的方法。

In the worst case, if it's too complicated, I would like to be able to store at least only the list of Tag items when I parse the Example Object : so I need a custom deserializer to get rid off the int total . 在最坏的情况下,如果它太复杂,我希望能够在解析示例对象时至少只存储Tag项列表:所以我需要一个自定义反序列化器来摆脱int total

So I would have : 所以我会:

class Foo{
    int userId;
    ArrayList<Tag> example;
    ...
}

I adopted the answer to present the full solution designed in chat and to fit to the changed JSON string. 我采用了答案来呈现聊天中设计的完整解决方案,以适应更改的JSON字符串。 The code assumes that the string json holds exactly the (updated) JSON from the question. 该代码假定字符串json完全包含问题中的(更新的)JSON。 The requirement is to fill the following class (setter and toString ommitted): 要求是填写以下类(setter和toString ommitted):

class Object1
{
    private String attribute1;
    private String attribute40;
    private int userId;
    private String  nameList;
}

GSON supports (as the most other REST-libs) three modes: GSON支持(作为大多数其他REST-libs)三种模式:

  • GSON_DOM GSON_DOM
    Reads the whole JSON via JsonParser.parse() and builds a DOM tree in memory (object model access). 通过JsonParser.parse()读取整个JSON,并在内存中构建DOM树(对象模型访问)。 Therefore this solution is good for small JSON files. 因此,此解决方案适用于小型JSON文件。
  • GSON_STREAM GSON_STREAM
    Reads only chunks of the JSON via JsonReader . 通过JsonReader只读取JSON的块。 Code is more complicated, but it is suited for large JSON files. 代码更复杂,但它适用于大型JSON文件。 As of Android 3.0 Honeycomb, GSON's streaming parser is included as android.util.JsonReader . 从Android 3.0 Honeycomb开始,GSON的流解析器包含在android.util.JsonReader
  • GSON_BIND GSON_BIND
    Databinding directly to classes via reflection, minimizes the code significantely. 通过反射直接数据绑定到类,最大限度地减少了代码。 GSON allows mixed mode, which means to combine GSON_DOM and GSON_BIND or GSON_STREAM and GSON_BIND which this answer should show. GSON允许混合模式,这意味着将GSON_DOM和GSON_BIND或GSON_STREAM和GSON_BIND结合起来,这个答案应该显示出来。

To fill the class Object1 via GSON_DOM and GSON_BIND the implementation looks like: 要通过GSON_DOM和GSON_BIND填充Object1类,实现如下:

private static void deserializeViaObjectAccess(final String json)
{
    Gson gson = new Gson();

    // Read the whole JSON into meomory via GSON_DOM
    JsonParser parser = new JsonParser();
    JsonObject object1 = parser.parse(json).getAsJsonObject().getAsJsonObject("object1");

    // map the Object1 class via GSON_BIND
    // (bind common attributes which exist in JSON and as properties in the class)
    // mapper acts as factory
    Object1 result = gson.fromJson(object1, Object1.class);

    // manually read the attribute from the user object
    int userId = object1.getAsJsonObject("user").getAsJsonPrimitive("id").getAsInt();
    result.setUserId(userId);

    // manually read the attributes from the example object
    String names = "";
    JsonArray list = object1.getAsJsonObject("example").getAsJsonArray("list");
    for (int i = 0; i < list.size(); ++i)
    {
        JsonObject entry = list.get(i).getAsJsonObject();
        String name = entry.getAsJsonPrimitive("name").getAsString();

        names = i == 0 ? name : names + "; " + name;
    }
    result.setNameList(names);

    // Output the result
    log.debug(result.toString());
}

To fill the class Object1 via GSON_STREAM and GSON_BIND the implementation looks like: 要通过GSON_STREAM和GSON_BIND填充Object1类,实现如下:

At the moment, this is only possible when a node is completly loaded via GSON_BIND or GSON_STREAM. 目前,这只有在通过GSON_BIND或GSON_STREAM完全加载节点时才有可能。 This example needs that a node itself should be splitted. 此示例需要分割节点本身。 This is only possible with the upcoming version 2.2. 这仅适用于即将推出的2.2版本。 I will hand the code in later when GSON 2.2 is available.* 稍后我会在GSON 2.2可用时提交代码。*

One option would be to parse the JSON string using the parser built inside Gson as detailed here . 其中一个选项是解析使用内部GSON内置详见解析器JSON字符串这里 You would do something like this: 你会做这样的事情:

com.google.gson.JsonParser parser = new JsonParser();
JsonObject object = parser.parse(data).getAsJsonObject();
JsonObject example = object.getAsJsonObject("example");
JsonArray list = example.getAsJsonArray("list");

JsonObject and JsonArray are part of Gson itself. JsonObjectJsonArray是Gson本身的一部分。

After using these you can use functions like getAsInt to parse individual fields and build and return whatever object you want. 使用这些之后,您可以使用getAsInt等函数来解析单个字段,并构建并返回您想要的任何对象。

Edit 1 编辑1

It does seem like that you can use gson.fromJson on a custom class too and not just the generic Java types as given in this example . 看起来你也可以在自定义类上使用gson.fromJson而不仅仅是本例中给出的通用Java类型。 So you have to parse your JSON string using parse and call fromJson on one of the inner objects or arrays. 因此,您必须使用parse解析JSON字符串,并在其中一个内部对象或数组上调用fromJson。

De-Serialize the example JSON into a full Example object, use the name properties of the Example object to build a String of the things you want, forget about the Example object. 将示例JSON序列化为完整的Example对象,使用Example对象的name属性构建所需事物的String,忘记Example对象。

I don't really understand the second question completely, but if you have a full Test1 object will all the fields/properties then you can create a Test2 object which takes the fields from Test1 which it wants. 我并不完全理解第二个问题,但如果你有一个完整的Test1对象将所有的字段/属性,那么你可以创建一个Test2对象,它从Test1获取它想要的字段。 Eg your Test2 object can accept Test1 as a parameter in its constructor and take only the properties which it needs ignoring the rest. 例如,您的Test2对象可以在其构造函数中接受Test1作为参数,并仅采用它忽略其余部分所需的属性。

While you're streaming in the Jsons over http, you can simply Discard the text and only store your custom objects. 当您通过http在Jsons中进行流式传输时,您可以简单地丢弃文本并仅存储自定义对象。 In this case, you will be continually discarding the unneeded information. 在这种情况下,您将不断丢弃不需要的信息。

While stream not empty  

    Read the next block into a new string

    Deserialize the string to the your object

    Store the object in a myArrayList

Note : reading the whole JSON and consuming it, as a whole, is most likely necessary if you want your application to be robust. 注意:如果您希望应用程序具有可靠性,则读取整个JSON并将其作为一个整体使用是非常必要的。 Unless you want to read the JSON as a raw character stream ( I doubt, unless your JSON is really, prohibitively big, that this shortcut is necessary). 除非您想将JSON读作原始字符流(我怀疑,除非您的JSON真的非常大,否则这个快捷方式是必要的)。

Neverthelss , reading the input stream without imposing and JSON well formedness requirements, could be done without ever having to write and unnecessary data structures to memory. Neverthelss,读取输入流而不强加和JSON良好构造要求,可以在不必编写和不必要的数据结构到内存的情况下完成。 This could work if you only want a small subset of the data- Ie You just want people's names, or urls in the JSON. 如果您只想要一小部分数据,这可能会有效 - 即您只需要人们的名字或JSON中的网址。 But it would break down if you want more complex data structures. 但如果您想要更复杂的数据结构,它就会崩溃。 Nevertheless : 不过:

// example parse urls from JSON lines without storing the whole data structure //示例解析来自JSON行的URL而不存储整个数据结构

While input stream is not empty

     String x = nextLine

     If x contains "http"

         myArrayList.add(parseUrl(x)

Final thoughts : 最后的想法 :

Bur ultimately, Jsons REST request are not like SQL- you cannot generucally and arbitrarily ignore certain fields.Perhaps , if you really want lighter weight Jsons, the more natural approach is to tell or check if your RESt service provider can simply broaden the types of RESt requests parameters to accommodate your use case. Bur最终,Jsons REST请求不像SQL那样 - 你不能在通用和任意地忽略某些领域。也许,如果你真的想要更轻量级的Jsons,更自然的方法是告诉或检查你的RESt服务提供商是否可以简单地扩大类型RESt请求参数以适应您的用例。

I would propose to use Jackson library. 我建议使用杰克逊图书馆。 It is provided with Apache license, so you can use it for commercial use free. 它提供Apache许可证,因此您可以免费使用它进行商业用途。 In tutorial look the capital "Streaming API Example". 教程中查看大写“Streaming API Example”。 It is very easy, and you control the streaming process totally. 它非常简单,您可以完全控制流式传输过程。 So, you can take what you want and ignore all other things. 所以,你可以拿走你想要的东西而忽略所有其他的东西。 Jackson library is divided in some jars. 杰克逊图书馆分为几个罐子。 The jar that supports streaming API, is the smallest and doesn't use any other. 支持流API的jar是最小的,不使用任何其他jar。 It is the answer, I think. 我想是答案。

Jackson can provide even more. 杰克逊可以提供更多。 In this article you can find the method to read the JSON file on the higher level, as elements, but with PREVIOUSLY setting what objects you need and what not. 本文中,您可以找到在更高级别上读取JSON文件的方法,作为元素,但是在PREVIOUSLY中设置您需要的对象和不需要的对象。 So, you can have as a result of parsing immediately only the elements you need. 因此,您可以立即解析所需的元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM