簡體   English   中英

如何通過修改結構反序列化Json?

[英]How do I deserialize Json with modifying the structure?

我有這個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,或者如果不可能,則可以使用GSON庫對Jackson進行反序列化? 一個將包含“人員”列表,另一個將包含“ 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結構與此處給出的示例不同,因此這是原始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庫以多種方式解析json。 我舉兩個例子。

方法1-編寫自定義解串器。 該技術使用一個類對個人對象進行反序列化。 自定義反序列化器允許您使用json數據創建所需的任何對象。 這是執行此操作所需的類:

Group.java:

public class Group {
    @SerializedName("people")
    private List<Person> persons;

    public List<Person> getPersons() {
        return persons;
    }

    public void setPersons(List<Person> persons) {
        this.persons = persons;
    }

    @Override
    public String toString() {
        String NEW_LINE = System.getProperty("line.separator");

        StringBuilder sb = new StringBuilder(this.getClass().getName());
        sb.append("{");
        sb.append(NEW_LINE);

        for(Person p : persons){
            sb.append(p.toString());
        }

        sb.append("}");
        return sb.toString();
    }
}

GsonTest.java:

public class GsonTest {

    public static void main(String[] args) {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Person.class, new PersonDeserializer());
        Gson gson = gsonBuilder.create();

        try {
            JsonParser parser = new JsonParser();
            Object obj = parser.parse(new FileReader("C://data.json"));
            JsonObject jsonObject = (JsonObject) obj;

            Group group = gson.fromJson(jsonObject, Group.class);
            System.out.println(group.toString());
        } catch (JsonIOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonSyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Person.java:

public class Person {

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

    private String name;
    private String sirname;
    private Long social_no;
    private Long creadit_card_no;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSirname() {
        return sirname;
    }

    public void setSirname(String sirname) {
        this.sirname = sirname;
    }

    public Long getSocial_no() {
        return social_no;
    }

    public void setSocial_no(Long social_no) {
        this.social_no = social_no;
    }

    public Long getCreadit_card_no() {
        return creadit_card_no;
    }

    public void Long(Long creadit_card_no) {
        this.creadit_card_no = creadit_card_no;
    }

    @Override
    public String toString() {
        String NEW_LINE = System.getProperty("line.separator");

        StringBuilder sb = new StringBuilder(this.getClass().getName());
        sb.append("{");
        sb.append(NEW_LINE);
        sb.append("name: ");
        sb.append(name);
        sb.append(NEW_LINE);

        sb.append("sirname: ");
        sb.append(sirname);
        sb.append(NEW_LINE);

        sb.append("social_no: ");
        sb.append(social_no);
        sb.append(NEW_LINE);

        sb.append("creadit_card_no: ");
        sb.append(creadit_card_no);
        sb.append(NEW_LINE);

        sb.append("}");
        sb.append(NEW_LINE);

        return sb.toString();
    }
}

PersonDeserializer.java

public class PersonDeserializer implements JsonDeserializer<Person> {

    public Person deserialize(JsonElement json, Type typeOfT,
        JsonDeserializationContext context) throws JsonParseException {

        JsonObject jsonObject = json.getAsJsonObject();

        String name = jsonObject.get("name").getAsString();
        String sirname = jsonObject.get("sirname").getAsString();

        JsonObject details = jsonObject.get("details").getAsJsonObject();

        Long social_no = details.get("social_no").getAsLong();
        Long creadit_card_no = details.get("creadit_card_no").getAsLong();

        Person person = new Person(name, sirname, social_no, creadit_card_no );

        return person;
    }
}

方法2-使用JsonReader類解析json數據。 您不必使用此技術一次加載整個json文件。 這是在資源有限的設備上解析大量數據的更好方法。 如果json結構發生變化,則此代碼將更難維護。 我的示例代碼受本文http://developer.android.com/reference/android/util/JsonReader.html的啟發。 將上面的Person類與此新的GsonTest類一起使用:

public class GsonTest {
    List<Person> people = null;

    public GsonTest() {
        people = new ArrayList<Person>();
    }

    public static void main(String[] args) {
        GsonTest gt = new GsonTest();

        gt.doGson();
    }

    void doGson() {
        try {
            InputStream is = GsonTest.class.getResourceAsStream("data.json");

            JsonReader jsonReader = new JsonReader(new InputStreamReader(is, "UTF-8"));

            jsonReader.beginObject();

            while (jsonReader.hasNext()) {
                String name = jsonReader.nextName();
                if (name.equals("people")) {
                    readPeopleArray(jsonReader);
                }
            }

            jsonReader.endObject();

            for(Person p : people){
                System.out.println(p.toString());
            }
        }
        catch (NullPointerException e){
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void readPeopleArray(JsonReader jsonReader) throws IOException {
        jsonReader.beginArray();
        while (jsonReader.hasNext()) {
            readPersonObject(jsonReader);
        }
        jsonReader.endArray();
    }

    private void readPersonObject(JsonReader jsonReader) throws IOException {
        String name = null;
        String sirname = null;
        Long social_no = null;
        Long creadit_card_no = null;

        jsonReader.beginObject();
        while(jsonReader.hasNext()){
            String key = jsonReader.nextName();

            if(key.equals("details")){

                jsonReader.beginObject();

                while(jsonReader.hasNext()){
                    String detailKey = jsonReader.nextName();

                    if(detailKey.equals("social_no")){
                        social_no = jsonReader.nextLong();
                    }
                    else if(detailKey.equals("creadit_card_no")){
                        creadit_card_no = jsonReader.nextLong();
                    }
                    else{
                        jsonReader.skipValue();
                    }
                }

                jsonReader.endObject();
            }
            else if(key.equals("name")){
                name = jsonReader.nextString();
            }
            else if(key.equals("sirname")){
                sirname = jsonReader.nextString();
            }
        }
        jsonReader.endObject();

        people.add(new Person(name, sirname, social_no, creadit_card_no));
    }
}

目前看來,Jackson不支持開箱即用的此類功能來映射嵌套路徑中的字段。 有一個懸而未決的問題 ,要求這樣的功能,但它是一個問題,什么時候能完成。 相反,可以通過使用@JsonUnwrapped批注將嵌套對象序列化為json中的第一級屬性。

因此,為了克服該問題,似乎唯一的方法是編寫一個自定義反序列化器,您可以將其映射到您的類,並根據需要使用它來創建該類的實例。

如果您有一個非常大的文件,建議您使用Gson使用自定義反序列化器進行此操作,但是我不使用JsonDeserializer接口; 使用TypeAdapter接口,因為它的性能更高( source )。 我認為@codemonkey有一個很好的答案,但是它過於復雜,可以更簡單地完成。 具體來說,您永遠不應該自己(使用sb.append() )構建這些String,並且應該遠離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引發Exception。 只是告訴它期望接下來會發生什么。

這是一些代碼,向您展示這個想法:

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.
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM