Java GSON:在反序列化时删除不必要的父对象

Java GSON:在反序列化时删除不必要的父对象,java,gson,json-deserialization,Java,Gson,Json Deserialization,我正在尝试使用GSON反序列化JSON数组。我的所有嵌套对象都嵌入到“嵌入”对象中 我也可能遇到这样的情况: { "Book": { "name": "Book 1", "published": 1999, "links": { "url": "www.book1.com" }, "embedded": { "Publisher": {

我正在尝试使用GSON反序列化JSON数组。我的所有嵌套对象都嵌入到“嵌入”对象中

我也可能遇到这样的情况:

{
    "Book": {
        "name": "Book 1",
        "published": 1999,
        "links": {
          "url": "www.book1.com"
        },
        "embedded": {
            "Publisher": {
                "name": "Publishing Company",
                "links": {
                    "url": "www.publishingcompany.com"
                }
            }
        }
    }
}
public class Book {
    String name;
    int published;
    String url;
    Author author;
    Publisher publisher;
}
这是一个非常简单的例子。我的一些对象可能嵌套2或3层深,并且都在“嵌入”对象中。此外,每个对象在“链接”对象中都有一个嵌套的“url”。我有大约20个不同的模型对象,每个对象都有几个字段,每个对象都有“嵌入式”对象。我开始为每个模型编写自定义反序列化程序,但这似乎忽略了使用gson的全部意义,而且我可能并不总是知道嵌入的对象是什么

我找到了这个,但它是用于序列化对象的。我已经试着弄明白这一点有一段时间了,但没有发现任何有效的方法

我的图书模型如下所示:

{
    "Book": {
        "name": "Book 1",
        "published": 1999,
        "links": {
          "url": "www.book1.com"
        },
        "embedded": {
            "Publisher": {
                "name": "Publishing Company",
                "links": {
                    "url": "www.publishingcompany.com"
                }
            }
        }
    }
}
public class Book {
    String name;
    int published;
    String url;
    Author author;
    Publisher publisher;
}
作者类别:

public class Author {
    String name;
    String url;
}
@Flatten("links::url")
private String url;
发布者类别:

public class Publisher {
    String name;
    String url;
}
这是我目前为止的图书反序列化程序:

public class BookDeserializer implements JsonDeserializer<Book> {
    @Override
    public Book deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        final JsonObject jsonObject = json.getAsJsonObject();

        Book book = new Book();
        book.setName(jsonObject.get("name").getAsString());
        book.setPublished(jsonObject.get("published").getAsInt());
        String url = jsonObject.getAsJsonObject("links").get("url").getAsString();
        book.setUrl(url);

        // 1) How to get rid of this and skip to the "real" nested object?
        final JsonObject embeddedObject = jsonObject.getAsJsonObject("embedded");

        // 2) See what the "embedded" object actually is.
        String embeddedModel;
        Set<Map.Entry<String, JsonElement>> entrySet = embeddedObject.entrySet();
        for (Map.Entry<String, JsonElement> entry : entrySet) {

            // Author or Publisher
            embeddedModel = entry.getKey();
        }

        // We have the model's key, now add code here to deserialize whatever the object is

        return book;
    }
}
公共类BookDeserializer实现JsonDeserializer{
@凌驾
PublicBook反序列化(JsonElement json,类型typeOfT,JsonDeserializationContext)引发JsonParseException{
final JsonObject JsonObject=json.getAsJsonObject();
书=新书();
setName(jsonObject.get(“name”).getAsString());
book.setPublished(jsonObject.get(“published”).getAsInt());
字符串url=jsonObject.getAsJsonObject(“链接”).get(“url”).getAsString();
setUrl(url);
//1)如何摆脱这种情况并跳到“真实”嵌套对象?
final JsonObject embeddedObject=JsonObject.getAsJsonObject(“嵌入式”);
//2)查看“嵌入”对象实际上是什么。
字符串嵌入模型;
Set entrySet=embeddedObject.entrySet();
for(Map.Entry:entrySet){
//作者或出版商
embeddedModel=entry.getKey();
}
//我们有了模型的密钥,现在在这里添加代码来反序列化对象
还书;
}
}
我仍然需要解析json并为Book设置每个字段。然后,我必须添加代码来确定并使用嵌套对象的正确反序列化器。看起来我仍然需要为每个对象使用自定义反序列化器来获取“url”。我对gson还比较陌生,所以可能有一些东西我忽略了,但我似乎也可以手动解析所有的json,甚至不用gson。也许有一种方法可以让json变得平坦


有没有关于如何解析它并仍然使用gson的便利性的想法,或者这是可能的?也许Jackson可以更好地处理这个问题?

创建一个名为embedded的类,并将其添加为Book中的字段:

public class Book {
    String name;
    int published;
    Embedded embedded;
}
然后创建一个嵌入式类:

public class Embedded {
    Author Author;
    Publisher Publisher;
}

只需根据JSON对类进行建模,我的第一个想法是解析JSON并对其进行破解,但它看起来GSON
JsonObject
s是不可变的

因此,我将编写一个简单的流解析器,查找
“嵌入式”:{
“链接”:{
并删除它们。也运行一个简单的括号计数器来删除匹配的右括号。如果时间允许,我可能会将一个放在一起

顺便说一句,您的示例JSON缺少一个逗号-请粘贴它以进行检查

添加:-流解析器失控了-尽管它会是一个更整洁的选项。如果你能找到一个JSON流解析器,就像SAX对XML所做的那样,你也许可以用这种方式做得更好

第二种机制假设您可以将整个JSON放在内存中的一个字符串中。这并不理想,但对于大多数设置来说可能是一种可接受的解决方案。然后使用简单的正则表达式加上括号计数器删除所需的部分

/**
 * Finds the first matching close brace - assuming an open brace has just been removed from the `start` position.
 */
private int closeBrace(StringBuilder s, int start) {
    int count = 1;
    boolean inQuotes = false;
    for (int i = start; i < s.length(); i++) {
        char ch = s.charAt(i);
        // Special case escapes.
        if (ch != '\\') {
            switch (ch) {
                case '"':
                    inQuotes = !inQuotes;
                    break;
                case '{':
                    if (!inQuotes) {
                        count += 1;
                    }
                    break;
                case '}':
                    if (!inQuotes) {
                        count -= 1;
                        if (count == 0) {
                            return i;
                        }
                    }
                    break;
            }
        } else {
            // Escape character - skip the next character.
            if (i < s.length()) {
                i += 1;
            }
        }
    }
    // Failed to find
    return s.length();
}

/**
 * Removes the JSON specified.
 */
private String hack(String json, String remove) {
    // Transfer to an sb for slicing and dicing.
    StringBuilder s = new StringBuilder(json);
    // Build my pattern
    Pattern p = Pattern.compile("\"" + remove + "\"\\s*:\\s*\\{");
    // Make my Matchjer.
    Matcher m = p.matcher(s);
    // Is it there?
    while (m.find()) {
        int start = m.start();
        int end = m.end();
        // Kill the match.
        s.delete(start, end);
        // Walk forward to find the close brace.
        end = closeBrace(s, start);
        // And remove it.
        if (end < s.length()) {
            s.delete(end, end + 1);
        }
        // Rebuild the matcher.
        m = p.matcher(s);
    }
    return s.toString();
}

private void test(String json) {
    JsonParser parser = new JsonParser();
    JsonElement e = parser.parse(json);
    System.out.println(e);
}

public void test() {
    String json = "{'Book': {'name': 'Book \\'1\\'','published': 1999,'links': {'url': 'www.book1.com'},'embedded': {'Publisher': {'name': 'Publishing Company','links': {'url': 'www.publishingcompany.com'}}}}}".replace("'", "\"");
    test(json);
    json = hack(json, "embedded");
    test(json);
    json = hack(json, "links");
    test(json);
}

这看起来有点像你要找的东西。

我想你在找这样的东西:

这个工具可以帮助你省略一些嵌入的类,你将拥有更少的类和更干净的代码。 在你的书本课上,使用以下方法:

@Flatten("embedded::Author")
private Author author;
这样可以折叠一层。 使用相同的方法可以将链接移动到作者。 在“作者”类中:

public class Author {
    String name;
    String url;
}
@Flatten("links::url")
private String url;
如果你想走得更深,你可以用同样的方法将两个级别移得更高。例如:

@Flatten("embedded::Author::name")
private String authorName;
在这里,您将在Book类中拥有作者的姓名


希望有帮助。

gson默认情况下不能反序列化和序列化此内容吗?为什么需要编写自己的?这是我最初认为的方式,但希望有一个更“优雅”的解决方案。创建链接和嵌入类似乎是浪费(还会有更多类似的),每个都有我甚至可能不使用的对象。我想这比为每个对象创建自定义反序列化器更容易。我们在公司中使用来在这些类之间映射。实际上,我们制作了一个平面响应对象,我们想要发送,并使用dozer来映射它们。这有助于自动将bean映射到彼此感谢您的帮助,我已经更正了JSON。这听起来是个好主意,一些示例代码会很棒!Gson确实支持流式处理,这就是您所指的吗?@Mark-见Added-这不是一个非常漂亮的解决方案,但它应该适用于中小型JSON对象。感谢您提供代码示例。不过,我将继续创建额外的类,以便java模型与json匹配。看起来gson可能没有提供内置的方法来打开选定的嵌套对象。请不要只是发布一些工具或库作为答案。至少在答案本身中进行演示。@Dwhitz感谢您的评论:)我编辑了答案。今天太忙了:/