java - 如何通过修改结构来反序列化Json?

标签 java android json jackson gson

我有这个 Json 内容:

{
    "people":[
        {
            "name":"test1",
            "sirname":"test2",
            "details":{
                "social_no":1234567,
                "creadit_card_no":34582342309
            }
        },
        {
            "name":"test3",
            "sirname":"test4",
            "details":{
                "social_no":12345679,
                "creadit_card_no":345823423090
            }
        }
    ]
}

根据逻辑,这个 Json 应该有 3 个 POJO 类:一个包含 People 列表、People 对象和一个 Details 对象的类。

现在我的问题是,是否可以使用 Jackson 反序列化此 Json,或者如果无法使用 Jackson 反序列化,则使用 GSON 库?一个包含 People 列表,另一个包含以下结构,例如 Human 类:

public class Human{

    String name;
    String sirname;
    String social_no;
    String creadit_card_no;
    //..getters and setters
    //should correspond with this json fragment:
      // {
      //  "name":"test1",
      //  "sirname":"test2",
      //  "details":{
      //    "social_no":1234567,
      //    "creadit_card_no":34582342309
      // }
    }
}

如果这是可能的,我该怎么做?

更新

我的实际 json 结构与此处给出的示例不同,因此 here is the original json

所以我自己创建了一个TypeAdapter,这是此类的代码:

public class PlanTypeAdapter extends TypeAdapter<Plan> {
    private final String TAG = PlanTypeAdapter.class.getSimpleName();

    @Override
    public void write(JsonWriter out, Plan value) throws IOException {
        Log.d(TAG, "WRITE");
    }

    @Override
    public Plan read(JsonReader reader) throws IOException {
        Log.d(TAG, "READ");
        Plan plan = new Plan();
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }

        reader.setLenient(false);
        while (reader.hasNext()) {
            Log.d(TAG, "PATH: " + reader.getPath());
            Log.d(TAG, "PEEK: " + reader.peek());
            if (reader.peek() == JsonToken.BEGIN_OBJECT) {
                Log.d(TAG, "BEGIN object, path: " + reader.getPath());
                reader.beginObject();
            } else if (reader.peek() == JsonToken.NULL) {
                Log.d(TAG, "NULL");
                reader.skipValue();
            } else if (reader.peek() == JsonToken.END_ARRAY) {
                Log.d(TAG, "END ARRAY");
                if (reader.getPath().contains("retailer")) {
                    reader.endObject();
                } else {
                    reader.endArray();
                }
            } else if (reader.peek() == JsonToken.END_OBJECT) {
                reader.endObject();
                Log.d(TAG, "END object, path: " + reader.getPath());
            } else if (reader.peek() == JsonToken.NUMBER) {
                Log.d(TAG, "NUMBER " + reader.getPath());
            } else if (reader.peek() == JsonToken.BOOLEAN) {
                Log.d(TAG, "BOOLEAN " + reader.getPath());
            } else if (reader.peek() == JsonToken.NAME) {
                switch (reader.nextName()) {
                    case "retailer":
                        reader.beginObject();
                        Log.d(TAG, "RET");
                        break;
                    case "national_plan":
                        reader.beginObject();
                        Log.d(TAG, "NPlan");
                        break;
                    case "name":
                        if (reader.getPath().contains("retailer")) {
                            plan.setRetailer_name(reader.nextString());
                            reader.skipValue();
                            reader.skipValue();
                            reader.endObject();
                        } else {
                            reader.skipValue();
                        }
                        break;
                    case "contract_end":
                        plan.setContract_end(reader.nextString());
                        break;
                    case "data_level_gb":
                        plan.setData_level_gb(reader.nextString());
                        break;
                    case "data_level_id":
                        plan.setData_level_id(reader.nextInt());
                        break;
                    case "days_to_end":
                        plan.setDays_to_switch(reader.nextInt());
                        break;
                    case "direct_from_operator":
                        plan.setDirect_from_operator(reader.nextBoolean());
                        break;
                    case "calculation_amount":
                        plan.setCalculationAmount(reader.nextDouble());
                        break;
                    case "network_generation_name":
                        plan.setNetwork_generation_(reader.nextString());
                        break;
                    case "partner_plan_id":
                        plan.setPartner_plan_id(reader.nextString());
                        break;
                    case "payment_level":
                        plan.setPayment_level(reader.nextString());
                        break;
                    case "payment_level_id":
                        plan.setPayment_level_id(reader.nextInt());
                        break;
                    case "roaming_amount":
                        plan.setRoaming_amount(reader.nextDouble());
                        break;
                    case "savings_amount":
                        plan.setSavings_amount(reader.nextDouble());
                        break;
                    case "savings_avg":
                        plan.setSavings_avg(reader.nextDouble());
                        break;
                    case "savings_percents":
                        plan.setSavings_percents(reader.nextInt());
                        break;
                    default:
                        Log.d(TAG, "DEFAULT " + reader.peek() + "");
                        reader.skipValue();
                        break;
                }

            } else {
                reader.skipValue();
            }
        }

        return plan;
    }
}

最佳答案

如果您有一个非常非常大的文件,我建议使用 Gson 使用自定义反序列化器来执行此操作,但我不会使用 JsonDeserializer 接口(interface);使用 TypeAdapter 接口(interface),因为它的性能更高 ( source )。我认为@codemonkey有一个非常好的答案,但它过于复杂,而且可以更简单地完成。具体来说,您永远不应该自己构建这些字符串(使用 sb.append()),并且应该远离 JsonDeserializer

首先,创建自定义TypeAdapter

public class PersonTypeAdapter extends TypeAdapter<Person> {
  @Override
  public void write(JsonWriter out, Person value) throws IOException {
    if (value == null) {
      out.nullValue();
      return;
    }

    out.beginObject();
    out.name("name").value(value.name);
    out.name("sirname").value(value.sirname);
    out.name("details");
    out.beginObject();
    out.name("social_no").value(value.social_no);
    out.name("creadit_card_no").value(value.creadit_card_no);
    out.endObject();
    out.endObject();
  }

  @Override
  public Person read(JsonReader reader) throws IOException {
    if (reader.peek() == JsonToken.NULL) {
      reader.nextNull();
      return null;
    }

    reader.beginObject();
    validateName(reader, "name");
    String name = reader.nextString();
    validateName(reader, "sirname");
    String sirname = reader.nextString();
    validateName(reader, "details");
    reader.beginObject();
    validateName(reader, "social_no");
    String social_no = reader.nextString();
    validateName(reader, "creadit_card_no");
    String creadit_card_no = reader.nextString();
    reader.endObject();
    reader.endObject();
    return new Person(name, sirname, social_no, creadit_card_no);
  }

  private void validateName(JsonReader reader, String string) throws IOException {
    String name = reader.nextName();
    if(!string.equals(name)) {
      throw new JsonSyntaxException("Expected: \"" + string + "\", got \"" + name + "\"");
    }
  }
}

而且,你的 POJO,显然是:

public class Person {
  public final String name;
  public final String sirname;
  public final String social_no;
  public final String creadit_card_no;

  public Person(String name, String sirname, String social_no,
      String creadit_card_no) {
    this.name = name;
    this.sirname = sirname;
    this.social_no = social_no;
    this.creadit_card_no = creadit_card_no;
  }

  @Override
  public String toString() {
    return String.format(
        "Person [name=%s, sirname=%s, social_no=%s, creadit_card_no=%s]", name,
        sirname, social_no, creadit_card_no);
  }
}

然后,您可以使用此处的方法从文件中解析 Json。 /test.json 只是您在问题中给出的示例。

import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;

public class PersonExample {
  public static void main(String... args) {
    InputStreamReader streamReader = new InputStreamReader(
        PersonExample.class.getResourceAsStream("/test.json"));

    PeopleWrapper wrapper = parseJSON(streamReader);
    System.out.println(wrapper.people);
  }

  public static class PeopleWrapper {
    @SerializedName("people")
    public List<Person> people;
  }

  public static PeopleWrapper parseJSON(Reader jsonInput) {
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Person.class, new PersonTypeAdapter());
    Gson gson = builder.create();

    PeopleWrapper peopleWrapper = gson.fromJson(jsonInput, PeopleWrapper.class);

    return peopleWrapper;
  }
}

该程序输出:

[Person [name=test1, sirname=test2, social_no=1234567, creadit_card_no=34582342309], Person [name=test3, sirname=test4, social_no=12345679, creadit_card_no=345823423090]]

所以你的实际问题比你最初描述的问题要复杂得多。我将向您展示您需要的 TypeAdapter 的框架,您可以弄清楚其余的内容。基本上,按照您所做的那样创建 Plan 对象,然后为每个外部 JSON 键处理值。

  • 如果是一行,只需在 switch 语句中处理即可。
  • 如果是数组或对象,创建一个辅助方法来解析 JSON 的该部分。

您应该假设 JSON 格式良好,如果不是,则让 Gson 抛出异常。只需告诉它期待接下来会发生什么。

这里有一些代码可以向您展示这个想法:

import java.io.IOException;

import com.google.gson.*;
import com.google.gson.stream.*;

public class PlanTypeAdapter extends TypeAdapter<Plan> {
    private final String TAG = PlanTypeAdapter.class.getSimpleName();

    @Override
    public void write(JsonWriter out, Plan value) throws IOException {
        Log.d(TAG, "WRITE");
    }

    @Override
    public Plan read(JsonReader reader) throws IOException {
        Log.d(TAG, "READ");
        Plan plan = new Plan();
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }

        reader.setLenient(false);
        reader.beginObject();

        while (!(reader.peek() == JsonToken.END_OBJECT)) {
            switch (reader.nextName()) {
            case "national_plan":
                handleNationalPlan(reader, plan);
                break;
            case "bill_total":
                handleBillTotal(reader, plan);
                break;
            case "contract_end":
                plan.setContract_end(reader.nextString());
                break;
            case "data_level_gb":
                plan.setData_level_gb(reader.nextString());
                break;
            case "data_level_id":
                plan.setData_level_id(reader.nextInt());
                break;
            case "days_to_end":
                plan.setDays_to_switch(reader.nextInt());
                break;
            case "direct_from_operator":
                plan.setDirect_from_operator(reader.nextBoolean());
                break;
            case "calculation_amount":
                plan.setCalculationAmount(reader.nextDouble());
                break;
            case "network_generation_name":
                plan.setNetwork_generation_(reader.nextString());
                break;
            case "partner_plan_id":
                plan.setPartner_plan_id(reader.nextString());
                break;
            case "payment_level":
                plan.setPayment_level(reader.nextString());
                break;
            case "payment_level_id":
                plan.setPayment_level_id(reader.nextInt());
                break;
            case "roaming_amount":
                plan.setRoaming_amount(reader.nextDouble());
                break;
            case "savings_amount":
                plan.setSavings_amount(reader.nextDouble());
                break;
            case "savings_avg":
                plan.setSavings_avg(reader.nextDouble());
                break;
            case "savings_percents":
                plan.setSavings_percents(reader.nextInt());
                break;
            case "yearly_id":
            case "handset":
            case "internals":
            case "consumer_id":
            case "calculation_details":
            case "operator":
            case "total":
            case "international_plan":
            case "contract_length":
            case "zone":
            case "externals":
            case "cancel_fee":
            case "transformers":
            case "one-offs":
            case "flow":
            case "roaming_plan":
            case "_id":
            // You can use this to ignore the keys you don't care about
            default:
                Log.d(TAG, "DEFAULT " + reader.peek() + "");
                reader.skipValue();
                break;
            }
        }

        reader.endObject();

        return plan;
    }

    private void handleNationalPlan(JsonReader reader, Plan plan) throws IOException {
        reader.beginObject();

        while (!(reader.peek() == JsonToken.END_OBJECT)) {
            switch(reader.nextName()) {
            case "contract_length":
                break;
            case "name":
                break;
            case "country":
            // etc.
            }
        }

        reader.endObject();
    }

    private void handleBillTotal(JsonReader reader, Plan plan) throws IOException {

    }

    // etc.
}

关于java - 如何通过修改结构来反序列化Json?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31751137/

相关文章:

java - SQL Server 和 Java 之间的时区代码转换

java - 如何在 Java 中使用 SortedMap 接口(interface)?

android - NavigationDrawer 仅使用 Android 支持库

java - getAction( ) 或 Intent 上的 NullPointerException

python - 使用 Python 对 JSON 字符串进行插值替换

json - 你如何在 Golang 中修改这个结构来接受两个不同的结果?

android - JSONObject 解析错误

java - 如何使用 Kotlin 使用路径播放 Mp3 文件?

java - 从按钮获取输入的 Android 单行 textView(用于计算器应用程序),这是一个问题吗?

java - 如何在 JPA 中定义单向 OneToMany 关系