如何使用Gson仅从Json反序列化某些特定字段?

时间:2017-04-24 12:22:19

标签: java json parsing gson

我有以下JSON字符串

 [
  {
    "channel": "/bvmt/initValues",
    "data": {
      "value": {
        "instrumentIds": "['TN0007250012','TN0007500010']",
        "instruments": "[{'mnemonic':'ADWYA','marche':'ADWYA','phaut':5.82,'open':5.82,'nbrTrans':7,'veille':5.82,'time':'11:14:28','recapChange':0.00,'state':'','variation':'.','ref':5.82,'stateGrp':'S','percentChange':-0.34,'last':5.80,'bestTransaction':[{'value':5.82,'qte':'3','time':'10:00:00'},{'value':5.82,'qte':'5','time':'10:02:26'},{'value':5.82,'qte':'145','time':'10:23:27'},{'value':5.81,'qte':'100','time':'10:23:42'},{'value':5.80,'qte':'1000','time':'10:23:42'},{'value':5.73,'qte':'1','time':'10:31:21'},{'value':5.80,'qte':'100','time':'11:14:28'}],'volume':7857.19,'id':'TN0007250012','bestLimits':[{'quantiteAchat':2600,'timeVente':'11:44:10','ordreAchat':1,'prixAchat':5.76,'quantiteVente':100,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.90},{'quantiteAchat':50,'timeVente':'11:44:10','ordreAchat':1,'prixAchat':5.74,'quantiteVente':210,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.95},{'quantiteAchat':250,'timeVente':'11:44:10','ordreAchat':2,'prixAchat':5.75,'quantiteVente':187,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.94},{'quantiteAchat':189,'timeVente':'11:44:10','ordreAchat':3,'prixAchat':5.73,'quantiteVente':1112,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.97},{'quantiteAchat':44,'timeVente':'11:44:10','ordreAchat':1,'prixAchat':5.72,'quantiteVente':400,'timeAchat':'11:44:10','ordreVente':1,'prixVente':5.98}],'openStatus':'','cto':0,'valuer':'ADWYA','pbas':5.73,'grp':'S','abrv':'ADWYA','houv':'','qto':0,'seuilBas':5.65,'vto':0,'quantite':1354,'seuilHaut':5.99},{'mnemonic':'WIFAK','marche':'WIFAK','phaut':7.11,'open':7.00,'nbrTrans':2,'veille':7.13,'time':'10:24:15','recapChange':0.00,'state':'','variation':'.','ref':7.13,'stateGrp':'S','percentChange':-0.28,'last':7.11,'bestTransaction':[{'value':7.00,'qte':'99','time':'10:17:00'},{'value':7.11,'qte':'100','time':'10:24:15'}],'volume':1404.00,'id':'TN0007200017','bestLimits':[{'quantiteAchat':100,'timeVente':'11:00:19','ordreAchat':1,'prixAchat':6.80,'quantiteVente':100,'timeAchat':'11:00:19','ordreVente':1,'prixVente':7.09},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':82,'timeAchat':'11:00:19','ordreVente':1,'prixVente':7.11},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':284,'timeAchat':'11:00:19','ordreVente':2,'prixVente':7.10},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':222,'timeAchat':'11:00:19','ordreVente':1,'prixVente':7.12},{'quantiteAchat':0,'timeVente':'11:00:19','ordreAchat':0,'prixAchat':0.00,'quantiteVente':110,'timeAchat':'11:00:19','ordreVente':2,'prixVente':7.13}],'openStatus':'','cto':0,'valuer':'WIFACK INT BANK','pbas':7.00,'grp':'S','abrv':'WIFAK','houv':'','qto':0,'seuilBas':6.92,'vto':0,'quantite':199,'seuilHaut':7.34}]"
      },
      "action": "initValues",
      "infoSession": {
        "lastInstrumentOrder": 11672,
        "state": 1,
        "lastInstrumentTime": "12:03:00",
        "tradingTime": "08:04:02",
        "tradingDate": "2017-04-24"
      }
    },
    "id": "5"
  },
  {
    "channel": "/bvmt/process",
    "successful": true,
    "id": "5"
  }
]

我只对“instruments”字段的内容感兴趣,我只想获得“助记符”和“percentChange”字段,并将它们反序列化为像这样的对象数组

public class Data 
{
    public List<MyObject> objects;
}

public class MyObject
{
    String mnemonic;
    Float percentChange;
}

如何使用Gson做到这一点?

1 个答案:

答案 0 :(得分:4)

实际上你已经十几种这样做了。它仅取决于您管理JSON文档的方式。让我们首先声明几个DTO:

final class Data {

    final List<MyObject> objects;

    Data(final List<MyObject> objects) {
        this.objects = objects;
    }

}
final class MyObject {

    final String mnemonic;
    final Float percentChange;

    MyObject(final String mnemonic, final Float percentChange) {
        this.mnemonic = mnemonic;
        this.percentChange = percentChange;
    }

}

以下是一些方法:

纯JsonElement树

以下示例使用Java 8 Stream API和Gson JSON树设施,这对我来说似乎是最简单的方法:

private static final Gson gson = new Gson();

static Data testUsingJsonTreesOnly(final Reader reader) {
    final List<MyObject> objects = StreamSupport.stream(gson.fromJson(reader, JsonElement.class).getAsJsonArray().spliterator(), false)
            .map(JsonElement::getAsJsonObject)
            .map(jsonObject -> jsonObject.get("data"))
            .filter(Objects::nonNull)
            .map(JsonElement::getAsJsonObject)
            .map(jsonObject -> jsonObject.get("value"))
            .filter(Objects::nonNull)
            .map(JsonElement::getAsJsonObject)
            .map(jsonObject -> jsonObject.get("instruments"))
            .filter(Objects::nonNull)
            .map(JsonElement::getAsJsonPrimitive)
            .map(JsonPrimitive::getAsString)
            .map(json -> gson.fromJson(json, JsonElement.class))
            .map(JsonElement::getAsJsonArray)
            .flatMap(jsonArray -> StreamSupport.stream(jsonArray.spliterator(), false))
            .map(jsonElement -> gson.fromJson(jsonElement, MyObject.class))
            .collect(toList());
    return new Data(objects);
}

双程映射

这种方法尝试在两遍中提取值:

  • 反序列化“外部”对象;
  • 获取内部对象字符串并在第二遍中反序列化。
private static final Gson gson = new Gson();

private static final Type channelViewListType = new TypeToken<List<ChannelView>>() {
}.getType();

private static final Type myObjectListType = new TypeToken<List<MyObject>>() {
}.getType();

static Data testUsingDeserializationWithStrings(final Reader reader) {
    final List<MyObject> objects = gson.<List<ChannelView>>fromJson(reader, channelViewListType)
            .stream()
            .filter(Objects::nonNull)
            .map(view -> view.data)
            .filter(Objects::nonNull)
            .map(view -> view.value)
            .filter(Objects::nonNull)
            .map(view -> view.instruments)
            .map((Function<String, List<MyObject>>) instruments -> gson.fromJson(instruments, myObjectListType))
            .flatMap(Collection::stream)
            .collect(toList());
    return new Data(objects);
}

private static final class ChannelView {

    final DataView data = null;

}

private static final class DataView {

    final ValueView value = null;

}

private static final class ValueView {

    final String instruments = null;

}

使用类型适配器的单遍映射

我想说,这就是第3级:你可以实现一个特定类型的适配器来“解包”编码的JSON文档。 @JsonAdapter可用于指定包含特定“内部”JSON文档的字段:

private static final Gson gson = new Gson();

private static final Type channelViewListType = new TypeToken<List<ChannelView>>() {
}.getType();

static Data testUsingDeserializationWithJsonAdapter(final Reader reader) {
    final List<MyObject> objects = gson.<List<ChannelView>>fromJson(reader, channelViewListType)
            .stream()
            .filter(Objects::nonNull)
            .map(view -> view.data)
            .filter(Objects::nonNull)
            .map(view -> view.value)
            .filter(Objects::nonNull)
            .map(view -> view.instruments)
            .flatMap(Collection::stream)
            .collect(toList());
    return new Data(objects);
}


private static final class ChannelView {

    final DataView data = null;

}

private static final class DataView {

    final ValueView value = null;

}

private static final class ValueView {

    @JsonAdapter(UnpackedJsonTypeAdapterFactory.class)
    final List<MyObject> instruments = null;

}

private static final class UnpackedJsonTypeAdapterFactory
        implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        return new UnpackedJsonTypeAdapter<>(gson.getAdapter(typeToken));
    }

    private static final class UnpackedJsonTypeAdapter<T>
            extends TypeAdapter<T> {

        private final TypeAdapter<T> typeAdapter;

        private UnpackedJsonTypeAdapter(final TypeAdapter<T> typeAdapter) {
            this.typeAdapter = typeAdapter;
        }

        @Override
        @SuppressWarnings("resource")
        public void write(final JsonWriter out, final T value)
                throws IOException {
            out.value(typeAdapter.toJson(value));
        }

        @Override
        public T read(final JsonReader in)
                throws IOException {
            final String json = in.nextString();
            final JsonReader lenientIn = new JsonReader(new StringReader(json));
            lenientIn.setLenient(true);
            return typeAdapter.read(lenientIn);
        }

    }

}

Pure streaming

可能是最简单的概念方式,但并不容易实现,因为创建了一个直接从头到尾处理JSON令牌流的高级解析器。请注意,不会引入偶数Gson个实例。

static Data testUsingStreaming(final Reader reader)
        throws IOException {
    final List<MyObject> myObjects = new ArrayList<>();
    final JsonReader jsonReader = new JsonReader(reader);
    jsonReader.beginArray();
    while ( jsonReader.hasNext() ) {
        jsonReader.beginObject();
        while ( jsonReader.hasNext() ) {
            switch ( jsonReader.nextName() ) {
            case "data":
                jsonReader.beginObject();
                while ( jsonReader.hasNext() ) {
                    switch ( jsonReader.nextName() ) {
                    case "value":
                        jsonReader.beginObject();
                        while ( jsonReader.hasNext() ) {
                            switch ( jsonReader.nextName() ) {
                            case "instruments":
                                final String instrumentsJson = jsonReader.nextString();
                                parseInstruments(instrumentsJson, myObjects);
                                break;
                            default:
                                jsonReader.skipValue();
                                break;
                            }
                        }
                        jsonReader.endObject();
                        break;
                    default:
                        jsonReader.skipValue();
                        break;
                    }
                }
                jsonReader.endObject();
                break;
            default:
                jsonReader.skipValue();
                break;
            }
        }
        jsonReader.endObject();
    }
    jsonReader.endArray();
    return new Data(myObjects);
}

private static void parseInstruments(final String instrumentsJson, final Collection<MyObject> myObjects)
        throws IOException {
    final JsonReader jsonReader = new JsonReader(new StringReader(instrumentsJson));
    jsonReader.setLenient(true);
    jsonReader.beginArray();
    while ( jsonReader.hasNext() ) {
        String mnemonic = null;
        Float percentChange = null;
        jsonReader.beginObject();
        while ( jsonReader.hasNext() ) {
            final String name = jsonReader.nextName();
            switch ( name ) {
            case "mnemonic":
                mnemonic = jsonReader.nextString();
                break;
            case "percentChange":
                percentChange = (float) jsonReader.nextDouble();
                break;
            default:
                jsonReader.skipValue();
                break;
            }
        }
        if ( mnemonic != null && percentChange != null ) {
            myObjects.add(new MyObject(mnemonic, percentChange));
        }
        jsonReader.endObject();
    }
    jsonReader.endArray();
}

上述所有方法都产生相同的输出:

  

ADWYA:-0.34
  WIFAK:-0.28