如何通过修改结构反序列化 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 classes:一个 class 将包含 People 列表、People 对象和一个 Details 对象。
现在我的问题是,是否可以使用 Jackson 反序列化此 Json,或者如果 Jackson 不可能,则使用 GSON 库?一个将包含人员列表,另一个将具有以下结构,例如 Human
class:
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
,这里是 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;
}
}
您可以通过多种不同的方式使用 gson 库解析 json。我举两个例子。
方法 1 - 编写自定义解串器。该技术使用 class 来反序列化 person 对象。自定义反序列化器允许您使用 json 数据创建任何您想要的对象。这是执行此操作所需的 classes:
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 class 解析 json 数据。您不必使用此技术一次加载整个 json 文件。这是在资源有限的设备上解析大量数据的更好方法。如果 json 结构发生变化,则此代码将更难维护。我的示例代码受到这篇文章 http://developer.android.com/reference/android/util/JsonReader.html 的启发。使用上面的 Person class 和这个新的 GsonTest class:
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 似乎不支持开箱即用的这种从嵌套路径映射字段的功能。
有一个 open issue 要求这样的功能,但它什么时候完成是个问题。
相反,使用 @JsonUnwrapped
注释可以将嵌套对象序列化到 json 中的第一级属性。
所以,为了克服这个问题,似乎唯一的方法是编写一个自定义反序列化器,您可以将其映射到您的 class,并使用它来创建 class 随心所欲。
如果您有一个非常非常大的文件,我建议使用 Gson, but I would not use the JsonDeserializer
interface; use the TypeAdapter
interface as it is more performant (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.
}
我有这个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 classes:一个 class 将包含 People 列表、People 对象和一个 Details 对象。
现在我的问题是,是否可以使用 Jackson 反序列化此 Json,或者如果 Jackson 不可能,则使用 GSON 库?一个将包含人员列表,另一个将具有以下结构,例如 Human
class:
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
,这里是 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;
}
}
您可以通过多种不同的方式使用 gson 库解析 json。我举两个例子。
方法 1 - 编写自定义解串器。该技术使用 class 来反序列化 person 对象。自定义反序列化器允许您使用 json 数据创建任何您想要的对象。这是执行此操作所需的 classes:
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 class 解析 json 数据。您不必使用此技术一次加载整个 json 文件。这是在资源有限的设备上解析大量数据的更好方法。如果 json 结构发生变化,则此代码将更难维护。我的示例代码受到这篇文章 http://developer.android.com/reference/android/util/JsonReader.html 的启发。使用上面的 Person class 和这个新的 GsonTest class:
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 似乎不支持开箱即用的这种从嵌套路径映射字段的功能。
有一个 open issue 要求这样的功能,但它什么时候完成是个问题。
相反,使用 @JsonUnwrapped
注释可以将嵌套对象序列化到 json 中的第一级属性。
所以,为了克服这个问题,似乎唯一的方法是编写一个自定义反序列化器,您可以将其映射到您的 class,并使用它来创建 class 随心所欲。
如果您有一个非常非常大的文件,我建议使用 Gson, but I would not use the JsonDeserializer
interface; use the TypeAdapter
interface as it is more performant (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.
}