1. Общ преглед
Тази статия ще ви покаже как да обработвате JSON, като използвате само основната Java EE, без да използвате зависимости на трети страни като Jersey или Jackson. Почти всичко, което ще използваме, се предоставя от пакета javax.json.
2. Записване на обект в JSON String
Преобразуването на Java обект в JSON String е супер лесно. Да приемем, че имаме прост клас Person :
public class Person { private String firstName; private String lastName; private Date birthdate; // getters and setters }
За да преобразуваме екземпляр от този клас в JSON String , първо трябва да създадем екземпляр на JsonObjectBuilder и да добавим двойки свойство / стойност, използвайки метода add () :
JsonObjectBuilder objectBuilder = Json.createObjectBuilder() .add("firstName", person.getFirstName()) .add("lastName", person.getLastName()) .add("birthdate", new SimpleDateFormat("DD/MM/YYYY") .format(person.getBirthdate()));
Забележете, че методът add () има няколко претоварени версии. Той може да получи повечето от примитивните типове (както и кутирани обекти) като втори параметър.
След като приключим със задаването на свойствата, просто трябва да запишем обекта в низ :
JsonObject jsonObject = objectBuilder.build(); String jsonString; try(Writer writer = new StringWriter()) { Json.createWriter(writer).write(jsonObject); jsonString = writer.toString(); }
И това е! Генерираният низ ще изглежда така:
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978"}
2.1. Използване на JsonArrayBuilder за изграждане на масиви
Сега, за да добавим малко повече сложност към нашия пример, нека приемем, че класът Person е модифициран, за да добави ново свойство, наречено имейли, което ще съдържа списък с имейл адреси:
public class Person { private String firstName; private String lastName; private Date birthdate; private List emails; // getters and setters }
За да добавим всички стойности от този списък към JsonObjectBuilder , ще ни е необходима помощта на JsonArrayBuilder :
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); for(String email : person.getEmails()) { arrayBuilder.add(email); } objectBuilder.add("emails", arrayBuilder);
Забележете, че използваме още една претоварена версия на метода add () , който приема обект JsonArrayBuilder като втори параметър.
И така, нека разгледаме генерирания String за обект Person с два имейл адреса:
{"firstName":"Michael","lastName":"Scott","birthdate":"06/15/1978", "emails":["[email protected]","[email protected]"]}
2.2. Форматиране на изхода с PRETTY_PRINTING
Така че успешно преобразувахме обект на Java в валиден JSON String . Сега, преди да преминем към следващия раздел, нека добавим малко просто форматиране, за да направим изхода по-"JSON-подобен" и по-лесен за четене.
В предишните примери създадохме JsonWriter, използвайки ясния Json . статичен метод createWriter () . За да получим повече контрол върху генерирания String , ще използваме способността на JsonWriterFactory на Java 7 да създава писател с конкретна конфигурация.
Map config = new HashMap(); config.put(JsonGenerator.PRETTY_PRINTING, true); JsonWriterFactory writerFactory = Json.createWriterFactory(config); String jsonString; try(Writer writer = new StringWriter()) { writerFactory.createWriter(writer).write(jsonObject); jsonString = writer.toString(); }
Кодът може да изглежда малко многословен, но наистина не прави много.
Първо, той създава екземпляр на JsonWriterFactory, предаващ конфигурационна карта на своя конструктор. Картата съдържа само един запис, който задава вярно на свойството PRETTY_PRINTING. След това използваме този фабричен екземпляр, за да създадем писател, вместо да използваме Json.createWriter () .
Новият изход ще съдържа отличителните прекъсвания на редове и таблици, които характеризират JSON String :
{ "firstName":"Michael", "lastName":"Scott", "birthdate":"06/15/1978", "emails":[ "[email protected]", "[email protected]" ] }
3. Изграждане на Java обект от низ
Сега нека направим обратната операция: конвертираме JSON String в Java обект.
Основната част от процеса на преобразуване се върти около JsonObject . За да създадете екземпляр на този клас, използвайте статичния метод Json.createReader (), последван от readObject () :
JsonReader reader = Json.createReader(new StringReader(jsonString)); JsonObject jsonObject = reader.readObject();
Методът createReader () приема InputStream като параметър. В този пример използваме StringReader, тъй като нашият JSON се съдържа в обект String , но същият този метод може да се използва за четене на съдържание от файл, например с помощта на FileInputStream .
С екземпляр на JsonObject под ръка можем да прочетем свойствата с помощта на метода getString () и да присвоим получените стойности на новосъздаден екземпляр от нашия клас Person :
Person person = new Person(); person.setFirstName(jsonObject.getString("firstName")); person.setLastName(jsonObject.getString("lastName")); person.setBirthdate(dateFormat.parse(jsonObject.getString("birthdate")));
3.1. Използването JsonArray да получите Списък Стойности
We'll need to use a special class, called JsonArray to extract list values from JsonObject:
JsonArray emailsJson = jsonObject.getJsonArray("emails"); List emails = new ArrayList(); for (JsonString j : emailsJson.getValuesAs(JsonString.class)) { emails.add(j.getString()); } person.setEmails(emails);
That's it! We have created a complete instance of Person from a Json String.
4. Querying for Values
Now, let's assume we are interested in a very specific piece of data that lies inside a JSON String.
Consider the JSON below representing a client from a pet shop. Let's say that, for some reason, you need to get the name of the third pet from the pets list:
{ "ownerName": "Robert", "pets": [{ "name": "Kitty", "type": "cat" }, { "name": "Rex", "type": "dog" }, { "name": "Jake", "type": "dog" }] }
Converting the whole text into a Java object just to get a single value wouldn't be very efficient. So, let's check a couple of strategies to query JSON Strings without having to go through the whole conversion ordeal.
4.1. Querying Using Object Model API
Querying for a property's value with a known location in the JSON structure is straightforward. We can use an instance of JsonObject, the same class used in previous examples:
JsonReader reader = Json.createReader(new StringReader(jsonString)); JsonObject jsonObject = reader.readObject(); String searchResult = jsonObject .getJsonArray("pets") .getJsonObject(2) .getString("name");
The catch here is to navigate through jsonObject properties using the correct sequence of get*() methods.
In this example, we first get a reference to the “pets” list using getJsonArray(), which returns a list with 3 records. Then, we use getJsonObject() method, which takes an index as a parameter, returning another JsonObject representing the third item in the list. Finally, we use getString() to get the string value we are looking for.
4.2. Querying Using Streaming API
Another way to perform precise queries on a JSON String is using the Streaming API, which has JsonParser as its main class.
JsonParser provides extremely fast, read-only, forward access to JS, with the drawback of being somewhat more complicated than the Object Model:
JsonParser jsonParser = Json.createParser(new StringReader(jsonString)); int count = 0; String result = null; while(jsonParser.hasNext()) { Event e = jsonParser.next(); if (e == Event.KEY_NAME) { if(jsonParser.getString().equals("name")) { jsonParser.next(); if(++count == 3) { result = jsonParser.getString(); break; } } } }
This example delivers the same result as the previous one. It returns the name from the third pet in the pets list.
Once a JsonParser is created using Json.createParser(), we need to use an iterator (hence the “forward access” nature of the JsonParser) to navigate through the JSON tokens until we get to the property (or properties) we are looking for.
Every time we step through the iterator we move to the next token of the JSON data. So we have to be careful to check if the current token has the expected type. This is done by checking the Event returned by the next() call.
There are many different types of tokens. In this example, we are interested in the KEY_NAME types, which represent the name of a property (e.g. “ownerName”, “pets”, “name”, “type”). Once we stepped through a KEY_NAME token with a value of “name” for the third time, we know that the next token will contain a string value representing the name of the third pet from the list.
This is definitely harder than using the Object Model API, especially for more complicated JSON structures. The choice between one or the other, as always, depends on the specific scenario you will be dealing with.
5. Conclusion
We have covered a lot of ground on the Java EE JSON Processing API with a couple of simple examples. To learn other cool stuff about JSON processing, check our series of Jackson articles.
Проверете изходния код на класовете, използвани в тази статия, както и някои модулни тестове, в нашето хранилище GitHub.