有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何使用Gson反序列化嵌套HashMap结构。fromJson

给定以下JSON源代码

{
  "database": "Accounts",
  "groups": {
    "databaseDictionary": {
      "folder": "C:\\Development\\Work\\Ac\\Data\\DataDictionary"
    },  
    "sqlCreateDatabase": {
      "name": "Sleaford",
      "user": "JBarrow",
      "useWindowsAuthentication": "true"
    },
    "defaultData": {
      "folder": "C:\\Development\\Work\\Ac\\Data\\Configuration"
    }
  }
}

我希望下面的类结构能够成功地填充对象

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;

public class JsonNestedHashMapConcise {
  private Configuration data;

  private class Configuration {
    private String database;
    private HashMap<String, Group> groups;

    private class Group {
      private HashMap<String, String> settings;
    }
  }

  public JsonNestedHashMapConcise (String fn) {
    try (Reader jsonFile = new BufferedReader(new FileReader(new File(fn)))) {
      Gson jsonParser = new Gson();
      this.data = jsonParser.fromJson(jsonFile, Configuration.class);
    }
    catch (JsonSyntaxException | IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    JsonNestedHashMapConcise myConfiguration;
    myConfiguration = new JsonNestedHashMapConcise("C:\\StackOverflow\\Concise.json");
    System.out.println("Completed reading database configuration for " + myConfiguration);
  }
}

但是,在配置的同时。组HashMap将填充嵌套组。设置哈希映射为空

我已经四处搜索并找到了一些可能的解决方案,比如JsonDeserializerregisterTypeAdapter&TypeToken但我不明白它们如何适合解决我的问题

作为一个背景,我有一个起点(下面列出了源代码和示例JSON),它提供了一个解决方案,但需要更详细的JSON语法。在编写支持方法时,我发现如果我进行配置。组字段aHashMap,这将导致更高效的查询。我还读到,Gson支持内部类,这个起点似乎支持这一说法

详细结构的JSON源代码

{
  "database": "Accounts",
  "groups": [
    {
      "name": "databaseDictionary",
      "settings": {
        "folder": "C:\\Development\\Work\\Ac\\Data\\DataDictionary"
    }  
    },{
      "name": "sqlCreateDatabase",
      "settings": {
        "name": "Sleaford",
        "user": "JBarrow",
        "useWindowsAuthentication": "true"
      }  
    },{
      "name": "defaultData",
      "settings": {
        "folder": "C:\\Development\\Work\\Ac\\Data\\Configuration"
      }  
    }
  ]
}

使用以下类结构

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;

public class JsonNestedHashMapVerbose {
  private Configuration data;

  private class Configuration {
    private String database;
    private ArrayList<Group> groups;

    private class Group {
      private String name;
      private HashMap<String, String> settings;
    }
  }

  public JsonNestedHashMapVerbose (String fn) {
    try (Reader jsonFile = new BufferedReader(new FileReader(new File(fn)))) {
      Gson jsonParser = new Gson();
      this.data = jsonParser.fromJson(jsonFile, Configuration.class);
    }
    catch (JsonSyntaxException | IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    JsonNestedHashMapVerbose myConfiguration;
    myConfiguration = new JsonNestedHashMapVerbose("C:\\StackOverflow\\Concise.json");
    System.out.println("Completed reading database configuration for " + myConfiguration);
  }

}

我能发现的两种设计之间的唯一区别是,起始结构在JSON中定义了一个显式的“settings”元素,解析器可能需要该元素才能反序列化结构的该部分

我是否将Gson反序列化程序推得太远,或者是否可以添加一些额外的代码来帮助Gson完成任务

对建议重复问题的反馈

在建议的相关问题中详细介绍的解决方案,使用由提供的解析器创建了一个非常详细的类结构,我在下面对此进行了详细介绍(为清晰起见进行了编辑)

这种自动解析器方法无法识别这些相关元素(databaseDictionary、sqlCreateDatabase和defaultData)都可以存储在HashMap结构中,如果解析器能够检测到这一点,我会非常惊讶。相反,我们最终为每个元素创建了一个类

public class DatabaseDictionary {
  public String folder;
}

public class DefaultData {
  public String folder;
}

public class Example {
  public String database;
  public Groups groups;
}

public class Groups {
  public DatabaseDictionary databaseDictionary;
  public SqlCreateDatabase sqlCreateDatabase;
  public DefaultData defaultData;
}

public class SqlCreateDatabase {
  public String name;
  public String user;
  public String useWindowsAuthentication;
}

本问题开头详述的JSON源代码是groups元素中存在的元素数量的子集,这些元素将被扩展(稍后再搜索)

使用详细使用的类结构需要创建一个新类来支持每个元素(我希望源代码中有10个或100个这些元素)。使用此结构还意味着很难在groups元素中找到指定的group元素以提取其属性

我可以使用JsonToken的基本方法来解析JSON源代码,以填充java类

private class Configuration {
    private String database;
    private HashMap<String, Group> groups;

    private class Group {
      private HashMap<String, String> settings;
    }
  }

但是,考虑到GSON解析器显然可以检测和填充HashMap元素,如第二个示例所示,我曾希望以相同的方式检测两层HashMap,以避免重复使用JsonToken,因为我有其他类似的JSON结构,可以从HashMap的使用中获益

我不知道GSON解析器是检测到提供的java类中的类类型,还是检测到在同一级别上有多个元素名,以便填充单个HashMap,因为我不知道GSON的内部工作原理

我希望有一个支持GSON抽象的方法,我可以用它来教育解析器填充分层HashMap

万一有人感兴趣

如果其他人想对类似的结构进行建模,我发现使用JsonReader类可以得到一个非常可读的解决方案,尽管如果toJson方法可以作为最简单的代码来编写会更好。我在下面提供了我的源代码,虽然我还是一个新手,但我相信可能有更优雅的方法来编写一些方法

import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;

public class DatabaseConfiguration {
  private String database;
  private HashMap<String, Group> groups;

  private class Group {
    private HashMap<String, String> settings;
  }

  private Group ReadGroup(JsonReader jsonReader) throws IOException {
   Group result = null;
   // Iterate over the the list of group attributes
   jsonReader.beginObject();
   while (jsonReader.hasNext()) {
     String name = jsonReader.nextName();
     if (result == null) {
       result = new Group();
       result.settings = new HashMap<>();
     }
     result.settings.put(name, jsonReader.nextString());
   }
   jsonReader.endObject();
   return result;
  }

  private HashMap<String, Group> ReadGroups (JsonReader jsonReader) throws IOException {
   HashMap<String, Group> result = null;

   // Iterate over the the list of groups
   jsonReader.beginObject();
   while (jsonReader.hasNext()) {
     String name = jsonReader.nextName();
     Group myGroup;
     myGroup = ReadGroup(jsonReader);
     if (result == null) {
       result = new HashMap<>();
     }
     result.put(name, myGroup);
   }
   jsonReader.endObject();
   return result;
  }

  public DatabaseConfiguration (String fn) {
    try (Reader jsonFile = new BufferedReader(new FileReader(new File(fn)));
         JsonReader jsonReader = new JsonReader(jsonFile)) {
     // Root node (database + groups)
     jsonReader.beginObject();
     while (jsonReader.hasNext()) {
       String name = jsonReader.nextName();
       switch (name) {
         case "database":
           this.database = jsonReader.nextString();
           break;
         case "groups":
           this.groups = ReadGroups(jsonReader);
           break;
         default:
           throw new JsonSyntaxException("Unexpected name " + name + "in outer element of " + fn);
       }
     }

     jsonReader.endObject();
    }
    catch (JsonSyntaxException | IOException e) {
      System.out.println(e);
    }
  }

  public static void main(String[] args) {
    DatabaseConfiguration myConfiguration;
    myConfiguration = new DatabaseConfiguration("C:\\StackOverflow\\Concise.json");
    System.out.println("Completed reading database configuration for " + myConfiguration);
  }
}

这是我第一次在这里发帖

我的原始问题最初被标记为重复问题,因此自动关闭。然后,按照建议,我编辑了我的问题详细说明为什么重复问题中的建议解决方案与我的问题无关。然后,我希望问题会被重新打开(因为它已经被编辑过),但这从未发生过。我不想剪切粘贴我编辑过的问题来重新发布,因为我觉得这会滥用堆栈溢出背后的原则,因此我对成功收回错误标记为重复的问题的正确方法感到困惑

由于我在上面发表了评论,我现在(偶然地)发现,我可以通过在评论中“ping”标记来请求“复制”标记的创建者审查他们的决定,所以我现在将尝试这样做

如果在叙述了为什么原来的问题不是表单上的重复问题之后,有一个明显的选择,那就太好了,但是因为我是一个新用户,在我有5个学分之前,我不能在普通论坛上发布任何反馈


共 (0) 个答案