我该如何反序列化的Json与修改结构? [英] How do I deserialize Json with modifying the structure?

查看:404
本文介绍了我该如何反序列化的Json与修改结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有此JSON内容:

  {
    人:[
        {
            名:测试1
            sirname:TEST2,
            细节:{
                social_no:1234567,
                creadit_card_no:34582342309
            }
        },
        {
            名:TEST3
            sirname:TEST4,
            细节:{
                social_no:12345679,
                creadit_card_no:345823423090
            }
        }
    ]
}

和按照逻辑此JSON应该有3 POJO类:将持有人名单的类,人物对象和细节的对象。

现在我的问题是,是否有可能反序列化此JSON使用杰克逊如果无法与杰克逊与GSON库?其中将包含人的名单,而另一个,例如类,这将具有以下结构:

 公共类人{    字符串名称;
    串sirname;
    串social_no;
    串creadit_card_no;
    //..getters和setter
    //应与此JSON片段是:
      // {
      //名:测试1
      //sirname:TEST2
      //细节:{
      //social_no:1234567,
      //creadit_card_no:34582342309
      //}
    }
}

因此​​,如果这是可能的,我怎么能这样做呢?

更新

我的实际工作的JSON结构比这里给出的例子不同,所以原来这里是JSON

所以,我对我自己创建了一个 TypeAdapter ,这里是从这个类code:

 公共类PlanTypeAdapter扩展TypeAdapter<计划和GT; {
    私人最终字符串标记= PlanTypeAdapter.class.getSimpleName();    @覆盖
    公共无效写入(JsonWriter出来,计划值)抛出IOException
        Log.d(TAG,写);
    }    @覆盖
    大众计划读(JsonReader阅读器)抛出IOException
        Log.d(TAG,读);
        规划方案=新的五年规划();
        如果(reader.peek()== JsonToken.NULL){
            reader.nextNull();
            返回null;
        }        reader.setLenient(假);
        而(reader.hasNext()){
            Log.d(TAG,路径:+ reader.getPath());
            Log.d(TAG,PEEK:+ reader.peek());
            如果(reader.peek()== JsonToken.BEGIN_OBJECT){
                Log.d(TAG,BEGIN对象,路径为:+ reader.getPath());
                reader.beginObject();
            }否则如果(reader.peek()== JsonToken.NULL){
                Log.d(TAG,空);
                reader.skipValue();
            }否则如果(reader.peek()== JsonToken.END_ARRAY){
                Log.d(TAG,结阵);
                如果(reader.getPath()。包含(零售商)){
                    reader.endObject();
                }其他{
                    reader.endArray();
                }
            }否则如果(reader.peek()== JsonToken.END_OBJECT){
                reader.endObject();
                Log.d(TAG,END对象,路径为:+ reader.getPath());
            }否则如果(reader.peek()== JsonToken.NUMBER){
                Log.d(TAG,数字+ reader.getPath());
            }否则如果(reader.peek()== JsonToken.BOOLEAN){
                Log.d(TAG,布尔+ reader.getPath());
            }否则如果(reader.peek()== JsonToken.NAME){
                开关(reader.nextName()){
                    案零售商:
                        reader.beginObject();
                        Log.d(TAG,RET);
                        打破;
                    案national_plan:
                        reader.beginObject();
                        Log.d(TAGNPlan);
                        打破;
                    案名:
                        如果(reader.getPath()。包含(零售商)){
                            plan.setRetailer_name(reader.nextString());
                            reader.skipValue();
                            reader.skipValue();
                            reader.endObject();
                        }其他{
                            reader.skipValue();
                        }
                        打破;
                    案contract_end:
                        plan.setContract_end(reader.nextString());
                        打破;
                    案data_level_gb:
                        plan.setData_level_gb(reader.nextString());
                        打破;
                    案data_level_id:
                        plan.setData_level_id(reader.nextInt());
                        打破;
                    案days_to_end:
                        plan.setDays_to_switch(reader.nextInt());
                        打破;
                    案direct_from_operator:
                        plan.setDirect_from_operator(reader.nextBoolean());
                        打破;
                    案calculation_amount:
                        plan.setCalculationAmount(reader.nextDouble());
                        打破;
                    案network_generation_name:
                        plan.setNetwork_generation_(reader.nextString());
                        打破;
                    案partner_plan_id:
                        plan.setPartner_plan_id(reader.nextString());
                        打破;
                    案payment_level:
                        plan.setPayment_level(reader.nextString());
                        打破;
                    案payment_level_id:
                        plan.setPayment_level_id(reader.nextInt());
                        打破;
                    案roaming_amount:
                        plan.setRoaming_amount(reader.nextDouble());
                        打破;
                    案savings_amount:
                        plan.setSavings_amount(reader.nextDouble());
                        打破;
                    案savings_avg:
                        plan.setSavings_avg(reader.nextDouble());
                        打破;
                    案savings_percents:
                        plan.setSavings_percents(reader.nextInt());
                        打破;
                    默认:
                        Log.d(TAG,DEFAULT+ reader.peek()+);
                        reader.skipValue();
                        打破;
                }            }其他{
                reader.skipValue();
            }
        }        返回计划;
    }
}


解决方案

如果你有一个非常,非常大的文件,我推荐一个自定义这样解串器使用的 GSON ,但我不会使用 JsonDeserializer 界面;使用 TypeAdapter 接口,因为它是更好的性能(<一个href=\"https://google-gson.google$c$c.com/svn/trunk/gson/docs/javadocs/com/google/gson/JsonDeserializer.html\"相对=nofollow>来源)。我认为,@ codemonkey有一个很好的答案,但它过于复杂,它可以简单地做更多的工作。具体来说,你永远不应该被这些builidng自己的字符串(与 sb.append()),你应该从 JsonDeserializer 。

首先,创建自定义 TypeAdapter

 公共类PersonTypeAdapter扩展TypeAdapter&LT;&人GT; {
  @覆盖
  公共无效写入(JsonWriter出来,人的价值)抛出IOException
    如果(价值== NULL){
      out.nullValue();
      返回;
    }    out.beginObject();
    。out.name(name)的值(value.name);
    。out.name(sirname)值(value.sirname);
    out.name(详细信息);
    out.beginObject();
    out.name(social_no)值(value.social_no)。
    。out.name(creadit_card_no)值(value.creadit_card_no);
    out.endObject();
    out.endObject();
  }  @覆盖
  公众人物的读取(JsonReader阅读器)抛出IOException
    如果(reader.peek()== JsonToken.NULL){
      reader.nextNull();
      返回null;
    }    reader.beginObject();
    validateName(读者,姓名);
    字符串名称= reader.nextString();
    validateName(读者sirname);
    串sirname = reader.nextString();
    validateName(读者,详细信息);
    reader.beginObject();
    validateName(读者social_no);
    串social_no = reader.nextString();
    validateName(读者creadit_card_no);
    串creadit_card_no = reader.nextString();
    reader.endObject();
    reader.endObject();
    返回新的Person(姓名,sirname,social_no,creadit_card_no);
  }  私人无效validateName(JsonReader读者,字符串字符串)抛出IOException
    字符串名称= reader.nextName();
    如果(!string.equals(名称)){
      抛出新JsonSyntaxException(预期:\\+字符串+\\,得到了\\+名字+\\);
    }
  }
}

和,你的POJO,很明显:

 公共类Person {
  公众最终字符串名称;
  公共最后弦乐sirname;
  公共最后弦乐social_no;
  公共最后弦乐creadit_card_no;  公众人物(字符串名称,字符串sirname,字符串social_no,
      字符串creadit_card_no){
    this.name =名称;
    this.sirname = sirname;
    this.social_no = social_no;
    this.creadit_card_no = creadit_card_no;
  }  @覆盖
  公共字符串的toString(){
    返回的String.format(
        人[名称=%S,sirname =%S,social_no =%S,creadit_card_no =%s]的,姓名,
        sirname,social_no,creadit_card_no);
  }
}

然后,您可以从您的文件在这里使用该方法解析JSON。 /test.json 就是你在你的问题给的例子。

 进口java.io.InputStreamReader中;
进口的java.io.Reader;
进口的java.util.List;
进口com.google.gson.Gson;
进口com.google.gson.GsonBuilder;
进口com.google.gson.annotations.SerializedName;公共类PersonExample {
  公共静态无效的主要(字符串参数... args){
    InputStreamReader中的StreamReader =新的InputStreamReader(
        PersonExample.class.getResourceAsStream(/ test.json));    PeopleWrapper包装= parseJSON(StreamReader的);
    的System.out.println(wrapper.people);
  }  公共静态类PeopleWrapper {
    @SerializedName(人)
    公开名单&LT;&人GT;人;
  }  公共静态PeopleWrapper parseJSON(读者jsonInput){
    GsonBuilder建设者=新GsonBuilder();
    builder.registerTypeAdapter(Person.class,新PersonTypeAdapter());
    GSON GSON = builder.create();    PeopleWrapper peopleWrapper = gson.fromJson(jsonInput,PeopleWrapper.class);    返回peopleWrapper;
  }
}

此程序输出:

[人物[姓名=为test1,sirname = TEST2,social_no = 1234567,creadit_card_no = 34582342309],人[名称= TEST3 ,sirname = TEST4,social_no = 12345679,creadit_card_no = 345823423090]


所以你的实际问题比最初描述的要复杂得多。我会告诉你的 TypeAdapter 骨骼你需要,你可以计算出其余。基本上,创建计划对象作为你做,然后为每个外JSON钥匙,办理值。


  • 如果这是一条线,你可以处理它在开关语句。

  • 如果它是一个数组或对象,创建的helper方法解析JSON的那部分。

您应当承担JSON结构良好,如果不是的话,让 GSON 抛出异常。只要告诉它什么期望会来下一个。

下面是一些code向您展示的理念是:

 进口java.io.IOException异常;导入com.google.gson *。
导入com.google.gson.stream *。公共类PlanTypeAdapter扩展TypeAdapter&LT;计划和GT; {
    私人最终字符串标记= PlanTypeAdapter.class.getSimpleName();    @覆盖
    公共无效写入(JsonWriter出来,计划值)抛出IOException
        Log.d(TAG,写);
    }    @覆盖
    大众计划读(JsonReader阅读器)抛出IOException
        Log.d(TAG,读);
        规划方案=新的五年规划();
        如果(reader.peek()== JsonToken.NULL){
            reader.nextNull();
            返回null;
        }        reader.setLenient(假);
        reader.beginObject();        而(!(reader.peek()== JsonToken.END_OBJECT)){
            开关(reader.nextName()){
            案national_plan:
                handleNationalPlan(读卡器,规划);
                打破;
            案bill_total:
                handleBillTotal(读卡器,规划);
                打破;
            案contract_end:
                plan.setContract_end(reader.nextString());
                打破;
            案data_level_gb:
                plan.setData_level_gb(reader.nextString());
                打破;
            案data_level_id:
                plan.setData_level_id(reader.nextInt());
                打破;
            案days_to_end:
                plan.setDays_to_switch(reader.nextInt());
                打破;
            案direct_from_operator:
                plan.setDirect_from_operator(reader.nextBoolean());
                打破;
            案calculation_amount:
                plan.setCalculationAmount(reader.nextDouble());
                打破;
            案network_generation_name:
                plan.setNetwork_generation_(reader.nextString());
                打破;
            案partner_plan_id:
                plan.setPartner_plan_id(reader.nextString());
                打破;
            案payment_level:
                plan.setPayment_level(reader.nextString());
                打破;
            案payment_level_id:
                plan.setPayment_level_id(reader.nextInt());
                打破;
            案roaming_amount:
                plan.setRoaming_amount(reader.nextDouble());
                打破;
            案savings_amount:
                plan.setSavings_amount(reader.nextDouble());
                打破;
            案savings_avg:
                plan.setSavings_avg(reader.nextDouble());
                打破;
            案savings_percents:
                plan.setSavings_percents(reader.nextInt());
                打破;
            案yearly_id:
            案手机:
            案内部:
            案consumer_id:
            案calculation_details:
            案经营者:
            案共:
            案international_plan:
            案contract_length:
            案区:
            案外部:
            案cancel_fee:
            案变形金刚:
            案一次性的:
            案流:
            案roaming_plan:
            案_id:
            //您可以使用此不理你不关心的钥匙
            默认:
                Log.d(TAG,DEFAULT+ reader.peek()+);
                reader.skipValue();
                打破;
            }
        }        reader.endObject();        返回计划;
    }    私人无效handleNationalPlan(JsonReader读卡器,规划计划)抛出IOException
        reader.beginObject();        而(!(reader.peek()== JsonToken.END_OBJECT)){
            开关(reader.nextName()){
            案contract_length:
                打破;
            案名:
                打破;
            案国:
            //等等。
            }
        }        reader.endObject();
    }    私人无效handleBillTotal(JsonReader读卡器,规划计划)抛出IOException    }    //等等。
}

I have this Json content:

{
    "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
            }
        }
    ]
}

and according to logic this Json should have 3 POJO classes: A class that will hold the list of People, People object and a Details object.

Now my question is, is it possible to deserialize this Json using Jackson or if not possible with Jackson, with GSON library? One that will contain the list of People, and another one, for example Human class, that will have the following structure:

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

So if this is possible, how can I do this?

Update

My actuall json structure is different than the example given here, so here is the original json

So I've created a TypeAdapter on my own, here is the code from this class:

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

解决方案

If you have a very, very large file, I recommend doing this with a custom deserializer using Gson, but I would not use the JsonDeserializer interface; use the TypeAdapter interface as it is more performant (source). I think that @codemonkey has a very good answer, but it is overly complicated and it can be done much more simply. Specifically, you should never be builidng these Strings yourself (with sb.append()) and you should stay away from JsonDeserializer.

First, create your custom 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 + "\"");
    }
  }
}

And, your POJO, obviously:

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);
  }
}

Then, you can parse the Json from your file using the method here. /test.json is just the example you gave in your question.

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

This program outputs:

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


So your actual problem is much more complicated than the one you originally described. I will show you a skeleton of the TypeAdapter you need, and you can figure out the rest. Basically, create the Plan object as you've done, and then for each of the outer JSON keys, handle the value.

  • If it's one line, you can just handle it in the switch statement.
  • If it's an array or an object, create a helper method to parse that section of the JSON.

You should assume the JSON is well formed and, if it's not, let Gson throw an Exception. Just tell it to expect what's going to come next.

Here's some code to show you the idea:

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

这篇关于我该如何反序列化的Json与修改结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆