Часто возникает потребность работы с Json, в частности его чтения и парсинга. В Java обычно ты знаешь с каким типом переменных работаешь, а при парсинге Json смущает то, что тип полей может быть любой.
Какие есть способы разбора Json? Как это делать?
Вот, допустим, как достать данные из Json, представленного ниже?
{
"firstName": "Json",
"lastName": "Smith",
"age": 30,
"address": {
"streetAddress": "666 1nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers": [
{
"type": "home",
"number": "542 666-1234"
},
{
"type": "fax",
"number": "653 666-4567"
}
],
"friends": [
{
"firstName": "Test",
"lastName": "Snow",
"age": 20,
"phoneNumbers": [
{
"type": "home",
"number": "141 111-1234"
}
],
"friends": [
{
"firstName": "UnknownFirstName",
"lastName": "UnknownLastName",
"age": 999,
"phoneNumbers": [
{
"type": "home",
"number": "000 000-0000"
}
]
}
]
},
{
"firstName": "Flash",
"lastName": "Tompson",
"age": 23,
"phoneNumbers": [
{
"type": "home",
"number": "999 111-1234"
}
]
}
]
}
Достать данные можно разными способами и, конечно, зависит от задач. Попробую рассмотреть некоторые варианты разбора Json.
Заметка: для каждого из примеров для парсинга будет взят Json из вопроса, чтобы зря не копировать в ответ.
Simple JsonГде взять: здесь / репозиторий на github / или через Maven и пр.
Это самый примитивный способ. По сути, всё, что тут есть - это JSONObject
и JSONArray
.
JSONArray
может включать в себя несколько объектов JSONObject
, его можно обходить циклом на каждой итерации получая объект JSONObject
.JSONObject
- объект, из которого можно доставать его отдельные свойства. Я бы использовал его для небольших Json строк, где не надо сильно заморачиваться или если не лень писать свой класс-обработчик на основе кода, который продемонстрирован ниже:
// Считываем json
Object obj = new JSONParser().parse(jsonString); // Object obj = new JSONParser().parse(new FileReader("JSONExample.json"));
// Кастим obj в JSONObject
JSONObject jo = (JSONObject) obj;
// Достаём firstName and lastName
String firstName = (String) jo.get("firstName");
String lastName = (String) jo.get("lastName");
System.out.println("fio: " + firstName + " " + lastName);
// Достаем массив номеров
JSONArray phoneNumbersArr = (JSONArray) jo.get("phoneNumbers");
Iterator phonesItr = phoneNumbersArr.iterator();
System.out.println("phoneNumbers:");
// Выводим в цикле данные массива
while (phonesItr.hasNext()) {
JSONObject test = (JSONObject) phonesItr.next();
System.out.println("- type: " + test.get("type") + ", phone: " + test.get("number"));
}
Остальная работа с вложенными массивами аналогична. Можно складывать в List, Map и пр.
Где взять: здесь / репозиторий на github / или через Maven и пр.
Документация: http://www.studytrails.com/java/json/java-google-json-introduction/
Позволяет парсить Json также, как и Json-simple, т.е. используя JSONObject
и JSONArray
(см. документацию), но имеет более мощный инструмент парсинга.
Достаточно создать классы, которые повторяют структуру Json'а. Для парсинга Json из вопроса создадим классы:
class Person {
public String firstName;
public String lastName;
public int age;
public Address address;
public List<Phones> phoneNumbers;
public List<Person> friends;
}
class Address {
public String streetAddress;
public String city;
public String state;
public int postalCode;
}
class Phones {
public String type;
public String number;
}
Теперь достаточно написать:
Gson g = new Gson();
Person person = g.fromJson(jsonString, Person.class);
Всё! Магия! Чудо! Теперь в person
лежит объект с типом Person
, в котором находятся данные именно с теми типами, которые были указаны в созданных классах!
Теперь можно работать с любым типом, как это привыкли всегда делать: String, Integer, List, Map и всё остальное.
// Выведет фамилии всех друзей с их телефонами
for (Person friend : person.friends) {
System.out.print(friend.lastName);
for (Phones phone : friend.phoneNumbers) {
System.out.println(" - phone type: " + phone.type + ", phone number : " + phone.number);
}
}
// output:
// Snow - phone type: home, phone number : 141 111-1234
// Tompson - phone type: home, phone number : 999 111-1234
Пример парсинга в Map
:
...... JSON для разбора:
{
"2":{
"sessions":[
{
"time":"13:00",
"price":"410"
},
{
"time":"06:40",
"price":"340"
},
{
"time":"16:50",
"price":"370"
}
],
"name":"Кинокис-L",
"locate":"Москва, Садовая-Спасская ул., 21, 56",
"metro":"Красные ворота"
},
"7":{
"sessions":[
{
"time":"06:35",
"price":"190"
},
{
"time":"00:05",
"price":"410"
}
],
"name":"Кинокис-V",
"locate":"Павелецкая пл., 2, строение 1",
"metro":"Павелецкая"
},
"8":{
"sessions":[
{
"time":"15:10",
"price":"330"
}
],
"name":"Кинокис-J",
"locate":"ул. Пречистенка, 40/2",
"metro":"Кропоткинская"
},
"9":{
"sessions":[
{
"time":"13:00",
"price":"600"
},
{
"time":"08:30",
"price":"300"
},
{
"time":"04:00",
"price":"510"
},
{
"time":"13:15",
"price":"340"
}
],
"name":"Кинокис-U",
"locate":"Шарикоподшипниковская ул., 24",
"metro":"Дубровка"
}
}
...... Классы (POJO):
class Seanse {
public String name;
public String locate
public String metro;
public List<Sessions> sessions;
}
class Sessions {
public String time;
public double price;
}
...... Сам разбор выглядит так:
Gson g = new Gson();
Type type = new TypeToken<Map<String, Seanse>>(){}.getType();
Map<String, Seanse> myMap = g.fromJson(json, type);
Всё.
Дополнительно в GSON можно использовать аннотации, например: исключить указанные поля при парсинге, поменять имя переменной (например не personFirstName
, а fName
) и многое другое. Подробнее см. в документации.
Где взять: здесь / репозиторий на github / или через Maven и пр.
Документация и примеры: https://github.com/FasterXML/jackson-docs
Как и GSON он также позволяет работать используя JSONObject
и JSONArray
если это требуется, и тоже умеет парсить на основе предоставленных классов (см. пример ниже).
Аналогично в нем можно указывать дополнительные требования за счет аннотаций, например: не парсить указанные поля, использовать кастомный конструктор класса, поменять имя переменной (например не firstName
, а fName
) и многое другое.
Подробнее см. в документации.
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(jsonString, Person.class);
System.out.println("My fio: " + person.firstName + " " + person.lastName + " and my friends are: ");
for (Person friend : person.friends) {
System.out.print(friend.lastName);
for (Phones phone : friend.phoneNumbers) {
System.out.println(" - phone type: " + phone.type + ", phone number : " + phone.number);
}
}
// output:
// My fio: Json Smith and my friends are:
// Snow - phone type: home, phone number : 141 111-1234
// Tompson - phone type: home, phone number : 999 111-1234
Где взять: через Maven и другие сборщики / репозиторий на github
Относится к так называемым XPath библиотекам. Её суть аналогична xpath
в xml, то есть легко получать часть информации из json'а, по указанному пути. А также позволяет фильтровать по условию.
// Выведет все фамилии друзей
List<String> friendsLastnames = JsonPath.read(jsonString, "$.friends[*].lastName");
for (String lastname : friendsLastnames) {
System.out.println(lastname);
}
// output:
// Snow
// Tompson
Пример с выборкой по условию:
// Поиск друга, которому больше 22 лет
List<String> friendsWithAges = JsonPath
.using(Configuration.defaultConfiguration())
.parse(jsonString)
.read("$.friends[?(@.age > 22)].lastName", List.class);
for (String lastname : friendsWithAges) {
System.out.println(lastname);
}
// output:
// Tompson
LoganSquare - основана на Jackson's streaming API. По демонстрируемым тестам работает быстрее GSON и Jackson. Поэтому хорош для Android.
Где взять: репозиторий на github / или через Maven / Gradle и пр.
Использование: https://github.com/bluelinelabs/LoganSquare#usage
@JsonObject
@JsonField
(с различными варианциями входных параметров, например @JsonField(name="message")
)Простенький пример:
@JsonObject
public class Person {
@JsonField(name="firstName")
public String firstName;
@JsonField(name="age")
public int age;
public void say() {
System.out.println();
System.out.println("My name is " + firstName + " , I'm " + age + " years old!");
}
}
разбор:
String jsonString = "{\"age\":15,\"firstName\":\"Adam\"}";
Person person = LoganSquare.parse(jsonString, Person.class);
person.say(); // My name is Adam , I'm 18 years old!
Moshi is a modern JSON library for Android and Java.
Хорош, как утверждают разработчики, для работы с Android.
Где взять: репозиторий на github / или через Maven / Gradle и пр.
Пример разбора Json строки в объект Person
:
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Person> jsonAdapter = moshi.adapter(Person.class);
Person person = jsonAdapter.fromJson(jsonStringPerson); // В person будут все данные
Пример парсинга в Map
:
class Seanse {
public String name;
public String locate
public String metro;
public List<Sessions> sessions;
}
class Sessions {
public String time;
public double price;
}
public class Main {
public static void main(String[] args) throws IOException {
String jsonStringForMap = "{\"2\":{\"sessions\":[{\"time\":\"13:00\",\"price\":\"410\"},{ \"time\":\"06:40\",\"price\":\"340\"},{ \"time\":\"16:50\",\"price\":\"370\"}],\"name\":\"Кинокис-L\",\"locate\":\"Москва, Садовая-Спасская ул., 21, 56\",\"metro\":\"Красные ворота\"},\"7\":{ \"sessions\":[ { \"time\":\"06:35\",\"price\":\"190\"},{ \"time\":\"00:05\",\"price\":\"410\"}],\"name\":\"Кинокис-V\",\"locate\":\"Павелецкая пл., 2, строение 1\",\"metro\":\"Павелецкая\"},\"8\":{ \"sessions\":[ { \"time\":\"15:10\",\"price\":\"330\"}],\"name\":\"Кинокис-J\",\"locate\":\"ул. Пречистенка, 40/2\",\"metro\":\"Кропоткинская\"},\"9\":{ \"sessions\":[ { \"time\":\"13:00\",\"price\":\"600\"},{ \"time\":\"08:30\",\"price\":\"300\"},{ \"time\":\"04:00\",\"price\":\"510\"},{ \"time\":\"13:15\",\"price\":\"340\"}],\"name\":\"Кинокис-U\",\"locate\":\"Шарикоподшипниковская ул., 24\",\"metro\":\"Дубровка\"}}";
Moshi moshi = new Moshi.Builder().build();
Type map = Types.newParameterizedType(Map.class, String.class, Seanse.class);
JsonAdapter<Map<String, Seanse>> jsonAdapter = moshi.adapter(map);
Map<String, Seanse> seanseMap = jsonAdapter.fromJson(jsonStringForMap);
}
}
Где взять: здесь / репозиторий на github / или через Maven и пр.
Документация: http://owlike.github.io/genson/Documentation/UserGuide/
За счет создания POJO (создаются классы, которые повторяют структуру Json'а) - парсится объект из строки, распихивая по нужным полям объектов. Есть возможность фильтровать свойства, включить или исключить поля при парсинге, переименовать, возможность работы с аннотациями и пр. Подробнее в документации.
Простой парсинг:
Genson genson = new Genson();
Person person = genson.deserialize(jsonString, Person.class); // В person будут все данные
Разбор в список:
List<Object> persons = genson.deserialize("[{\"age\":28,\"name\":\"Foo\"}, {\"age\":30,\"name\":\"Bar\"}]", List.class);
// persons - список с данными объектов
Пример парсинга в Map
:
...... JSON для разбора:
{
"2":{
"sessions":[
{
"time":"13:00",
"price":"410"
},
{
"time":"06:40",
"price":"340"
},
{
"time":"16:50",
"price":"370"
}
],
"name":"Кинокис-L",
"locate":"Москва, Садовая-Спасская ул., 21, 56",
"metro":"Красные ворота"
},
"7":{
"sessions":[
{
"time":"06:35",
"price":"190"
},
{
"time":"00:05",
"price":"410"
}
],
"name":"Кинокис-V",
"locate":"Павелецкая пл., 2, строение 1",
"metro":"Павелецкая"
},
"8":{
"sessions":[
{
"time":"15:10",
"price":"330"
}
],
"name":"Кинокис-J",
"locate":"ул. Пречистенка, 40/2",
"metro":"Кропоткинская"
},
"9":{
"sessions":[
{
"time":"13:00",
"price":"600"
},
{
"time":"08:30",
"price":"300"
},
{
"time":"04:00",
"price":"510"
},
{
"time":"13:15",
"price":"340"
}
],
"name":"Кинокис-U",
"locate":"Шарикоподшипниковская ул., 24",
"metro":"Дубровка"
}
}
...... Классы (POJO):
class Seanse {
public String name;
public String locate
public String metro;
public List<Sessions> sessions;
}
class Sessions {
public String time;
public double price;
}
...... Сам парсинг:
String jsonStringForMap = "ТУТ JSON СТРОКА, ОПИСАННАЯ ВЫШЕ";
Genson genson = new Genson();
Map<String, Seanse> seansesMap = genson.deserialize(jsonStringForMap, Map.class);
Где взять: через Maven и другие сборщики / репозиторий на github. Непосредственно описание работы с xpath. Осторожно, ̶н̶е̶н̶о̶р̶м̶а̶т̶и̶в̶н̶а̶я̶ ̶л̶е̶к̶с̶и̶к̶а̶ китайский язык.
Относится к XPath аналогам.
Обычный способ:
Person person = JSON.parseObject(jsonString, Person.class);
int age = person.age;
System.out.println(age); // 30
Применение XPpath
// Выведет все фамилии друзей
List<String> friendsLastnames = (List<String>) JSONPath.eval(person, "$.friends.lastName");
for (String lastname : friendsLastnames) {
System.out.println(lastname); // Snow Tompson
}
// Поиск друга, которому больше 22 лет
List<String> friendsWithAges = (List<String>) JSONPath.eval(person, "$.friends[?(@.age > 22)].lastName");
for (String lastname : friendsWithAges) {
System.out.println(lastname); // Tompson
}
Поддерживает сериализацию и парсинг JSON без предварительного маппинга в классах:
Maven:
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.2</version>
</dependency>
Пример разбора строки JSON:
public static void main(String[] args) throws IOException {
JsonReader reader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = reader.readObject();
}
Пример вывода объекта в строку JSON:
public static void main(String[] args) throws IOException {
System.out.println(prettyPrintJson(jsonObject, 0));
}
public static String prettyPrintJson(JsonObject jsonObject, int indent) {
String indentStr = getIndentStr(indent);
String prettyJson = indentStr + "{";
for (String key : jsonObject.keySet()) {
prettyJson += "\n";
prettyJson += indentStr + " \"" + key + "\": ";
try {
JsonArray jsonArray = jsonObject.get(key).asJsonArray();
prettyJson += "\n" + indentStr + " [";
for (JsonValue element : jsonArray) {
prettyJson += "\n" + prettyPrintJson(element.asJsonObject(), indent + 4);
prettyJson += ",";
}
prettyJson = prettyJson.substring(0, prettyJson.length() - 1);
prettyJson += "\n" + indentStr + " ]";
} catch (Exception e) {
try {
prettyJson += "\n" + prettyPrintJson(jsonObject.get(key).asJsonObject(), indent + 2);
} catch (Exception ee) {
prettyJson += jsonObject.get(key).toString();
}
}
prettyJson += ",";
}
prettyJson = prettyJson.substring(0, prettyJson.length() - 1);
prettyJson += "\n" + indentStr + "}";
return prettyJson;
}
public static String getIndentStr(int indent) {
String indentStr = "";
for (int i = 0; i < indent; i++) {
indentStr += " ";
}
return indentStr;
}
Вывод:
{
"firstName": "Json",
"lastName": "Smith",
"age": 30,
"address":
{
"streetAddress": "666 1nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumbers":
[
{
"type": "home",
"number": "542 666-1234"
},
{
"type": "fax",
"number": "653 666-4567"
}
],
"friends":
[
{
"firstName": "Test",
"lastName": "Snow",
"age": 20,
"phoneNumbers":
[
{
"type": "home",
"number": "141 111-1234"
}
],
"friends":
[
{
"firstName": "UnknownFirstName",
"lastName": "UnknownLastName",
"age": 999,
"phoneNumbers":
[
{
"type": "home",
"number": "000 000-0000"
}
]
}
]
},
{
"firstName": "Flash",
"lastName": "Tompson",
"age": 23,
"phoneNumbers":
[
{
"type": "home",
"number": "999 111-1234"
}
]
}
]
}
Здесь расположена общая информация о парсерах, которая может помочь при выборе и понять, что он умеет. Текст и таблица, представленные ниже, взяты из публикации на Habrahabr: Шпаргалка Java программиста 8. Библиотеки для работы с Json, автор статьи @ВеденинВячеслав
Существует следующие способы сериализации и десериализации среди указанных библиотек (от самого простого к самому сложному):
Самый популярный и простой способ - вы просто указываете класс, который нужно преобразовать в json, может быть часть полей отмечаете аннотациями (а зачастую даже это необязательно), а библиотека сама превращает этот класс и всю его иерархию классов в json.
Плюсы: наиболее простой из всех
Минусы: скорость и память. Большинство библиотек использует рефлексию и т.п. методы работы с Java классами (хотя не все), что очевидно не очень быстро. К тому же, весь json файл сразу превращается в Java объекты, что может просто исчерпать всю доступную память, если вы попытаетесь обработать очень большой json.
Вывод: если нет проблем с производительностью, памятью и вы не собираетесь обрабатывать многогигабайтные json'ы скорее всего самый лучший способ.
Данный парсер представляет json в виде Java классов таких как
Node
или `JsonElement c иерархической структурой, а уже сам
программист их обходит и получает из них информацию.
Плюсы: обычно быстрее первого способа и проще третьего
Минусы: уступает Data bind по простоте, плюс ряд библиотек способен генерить классы при Data bind, а не использовать рефлексию, в этом случае то что Tree Model будет быстрее не очевидно, к тому же не решается проблема огромных файлов и ограничения памяти.
Самый низкоуровневый способ, по сути программист сам вручную разбирает токены json'a. Зато никаких ограничений по памяти и в теории максимальная производительность.
Плюсы: производительность и минимальное потребление памяти
Минусы: сложность использования
Не очень подходит, если нужно получит всю информацию из json'a, зато
позволяет написав выражение, например $.store.book[*].author
получить список всех авторов всех книг из json'a магазина. То есть легко получать
часть информации из json'а.
Плюсы: позволяет быстро получить информацию из json'а по сложным критериям
Минусы: не очень подходит, когда нужна все информация из json'а, не работает в обратную сторону на формирования json'ов
Таблица библиотек и способы парсинга, которые они поддерживают:
Способ\Hазвание Fastjson Gson LoganSquare JSONjava Moshi Jackson Genson JsonPath SimpleJson
Data bind Да Да Да - Да Да Да - -
Tree Model - Да - Да - Да - - Да
Streaming API - Да - - - Да - - -
Аналоги XPath Да - - - - - - Да -
Генерация классов - - Да - - - - -
для Data bind*
Работает со Да Да Нет - Да Да Да -
static inner class**
Обязательность Нет Нет Да - Нет Нет Нет -
аннотаций***
* — Генерация классов для Data bind позволяет сгенерировать классы на стадии компиляции, что в теории должно давать значительный прирост производительности библиотеки,
** — Работает со static inner class имеет смысл только для случая Data bind, возможно ли сериализация и десериализация для случая статических внутренних классов (не статические внутренние классы сериализовать не рекомендуется),
*** — тоже только для случая Data bind можно ли не использовать аннотации или их использование крайне рекомендуется,
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Можно ли сразу настроить авторизацию чтобы не запрашивать авторизацию в пользователей, а создавать календари, события на своем аккаунте?
Не понятно каким образом сравнивать значение зарплаты в методе
Мне нужно в сокет отправить строкуПри этом, в пакете вначале должно идти длина пакета в байтах
На разметке активности есть ListView и EditTextПри вводе символов пользователем в ListView должны оставаться только объекты которые содержат введенные...