Google Gson反序列化问题

时间:2018-08-28 15:12:09

标签: java android json gson deserialization

我有一个Java类:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Filescan {

    public static void main(String[] args) throws IOException {
        Filescan sc = new Filescan();
        sc.findWords("src/main/resources/files", new String[]{"author", "book"}, true);
    }

    // kind of Tuple/Map.Entry
    static class Pair<K,V>{
        final K key;
        final V value;

        Pair(K key, V value){
            this.key = key;
            this.value = value;
        }

        @Override
        public String toString() {
            return key + " " + value;
        }
    }

    public void findWords(String directory, String[] words, boolean ignorecase) throws IOException{

        final String[] searchWords = ignorecase ? toLower(words) : words;

        try (Stream<Path> stream =     Files.walk(Paths.get(directory)).filter(Files::isRegularFile)) {
            long startTime = System.nanoTime();
            List<Pair<Path,Map<String, List<Integer>>>> result = stream
                    // you can test it with parallel execution, maybe it is faster
                    .parallel()
                    // searching
                    .map(path -> findWordsInFile(path, searchWords, ignorecase))
                    // filtering out empty optionals
                    .filter(Optional::isPresent)
                    // unwrap optionals
                    .map(Optional::get).collect(Collectors.toList());
            System.out.println("Time taken = " +     TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()
                            - startTime) + " seconds");
            System.out.println("result:");
            result.forEach(System.out::println);
        }
    }

    private String[] toLower(String[] words) {
        String[] ret = new String[words.length];
        for (int i = 0; i < words.length; i++) {
            ret[i] = words[i].toLowerCase();
        }
        return ret;
    }

    private static Optional<Pair<Path,Map<String, List<Integer>>>>     findWordsInFile(Path path, String[] words, boolean ignorecase) {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(path.toFile())))) {
            String line = br.readLine();
            line = ignorecase & line != null ? line.toLowerCase() : line;
            Map<String, List<Integer>> map = new HashMap<>();
            int linecount = 0;
            while(line != null){
                for (String word : words) {
                    if(line.contains(word)){
                        if(!map.containsKey(word)){
                            map.put(word, new ArrayList<Integer>());
                        }
                        map.get(word).add(linecount);
                    }
                }
                line = br.readLine();
                line = ignorecase & line != null ? line.toLowerCase() : line;
                linecount++;
            }
            if(map.isEmpty()){
                // returning empty optional when nothing in the map
                return Optional.empty();
            }else{
                // returning a path-map pair with the words and the rows where each word has been found
                return Optional.of(new Pair<Path,Map<String, List<Integer>>>(path, map));
            }
        } catch (IOException ex) {
            throw new UncheckedIOException(ex);
        }
    }    
}

我已经使用Gson将一些Object1实例保存为JSON字符串:

public class Object1 {
    private int field1;
    private String field2;
    private Object2 object2;
    private boolean field3;
}

然后我向Object1类添加了新的String字段:

    String jsonString = new Gson().toJson(object1, Object1.class);

现在我无法使用方法将json字符串反序列化为Object1实例:

public class Object1 {
    private int field1;
    private String field2;
    private String field4;
    private Object2 object2;
    private boolean field3;
}

由于Gson引发异常:

  

System.err:com.google.gson.JsonSyntaxException:java.lang.IllegalStateException:预期为字符串,但在第1行444列$ .c处为BEGIN_OBJECT           在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.read(ReflectiveTypeAdapterFactory.java:224)           在com.google.gson.Gson.fromJson(Gson.java:887)           在com.google.gson.Gson.fromJson(Gson.java:852)           在com.google.gson.Gson.fromJson(Gson.java:801)           在com.google.gson.Gson.fromJson(Gson.java:773)

但是为什么呢?我有没有一个字段的JSON字符串,这不是问题。为什么我不能反序列化?

3 个答案:

答案 0 :(得分:0)

Expected a string but was BEGIN_OBJECT

field4中的json字符串不是String类型,请使用Json to POJO生成器来创建适当的对象。

我喜欢使用http://www.jsonschema2pojo.org/

答案 1 :(得分:0)

@ user523392说:

成员变量必须与JSON响应中给出的完全匹配

不是这种情况。

有一些选项可用于指定Java字段名称如何映射到JSON元素名称。

一种适用于上述原始问题的解决方案是用@SerializedName注释Java类成员,以非常明确地声明其映射到的JSON元素名称。

// output: [MyObject: element=value1, elementTwo=value2]

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;

public class Foo
{
  static String jsonInput =
      "{" +
          "\"element\":\"value1\"," +
          "\"@element-two\":\"value2\"" +
      "}";

  public static void main(String[] args)
  {
    GsonBuilder gsonBuilder = new GsonBuilder();
    Gson gson = gsonBuilder.create();
    MyObject object = gson.fromJson(jsonInput, MyObject.class);
    System.out.println(object);
  }
}

class MyObject
{
  String element;

  @SerializedName("@element-two")
  String elementTwo;

  @Override
  public String toString()
  {
    return String.format(
        "[MyObject: element=%s, elementTwo=%s]",
        element, elementTwo);
  }
}

另一种方法是创建一个自定义FieldNamingStrategy,以指定如何将Java成员名称转换为JSON元素名称。本示例将相同的名称映射应用于所有Java成员名称。这种方法不适用于上面的原始示例,因为并非所有JSON元素名称都遵循相同的命名模式-它们并非都以'@'开头,并且有些使用驼峰式命名而不是使用'-来分隔名称部分'。构建Gson实例(gsonBuilder.setFieldNamingStrategy(new MyFieldNamingStrategy());)时,将使用此FieldNamingStrategy的实例。

class MyFieldNamingStrategy implements FieldNamingStrategy
{
  // Translates the field name into its JSON field name representation.
  @Override
  public String translateName(Field field)
  {
    String name = field.getName();
    StringBuilder translation = new StringBuilder();
    translation.append('@');
    for (int i = 0, length = name.length(); i < length; i++)
    {
      char c = name.charAt(i);
      if (Character.isUpperCase(c))
      {
        translation.append('-');
        c = Character.toLowerCase(c);
      }
      translation.append(c);
    }
    return translation.toString();
  }
}

管理Java字段名称如何映射到JSON元素名称的另一种方法是在构建Gson实例时指定FieldNamingPolicy,例如gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);。但是,这也不适用于原始示例,因为它对所有情况都应用了相同的名称映射策略。

答案 2 :(得分:0)

事实证明,问题出在混淆之中。

如果您不使用@SerializedName注释结果,则JSON可能如下所示:

  

{“ a”:3436213,“ b”:“某些字符串”,“ c”:{.............},“ d”:true}

我们没有使用它,因为它不是DTO。在这种情况下,我们仅使用JSON来存储一些不重要的内部数据。但这对我来说是非常有趣的一课。