Как и чем парсить Json на Java?

541
17 ноября 2017, 06:36

Здравствуйте!

Часто возникает потребность работы с 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"
                }
            ]
        }
    ]
}
Answer 1

Достать данные можно разными способами и, конечно, зависит от задач. Попробую рассмотреть основные.

Заметка: для каждого из примеров для парсинга будет взят 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 и пр.

GSON

Где взять: здесь / репозиторий на 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 ArrayList<Phones> phoneNumbers;
    public ArrayList<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

Дополнительно можно использовать аннотации, например: исключить указанные поля при парсинге, поменять имя переменной (например не personFirstName, а fName) и многое другое. Подробнее см. в документации.

Jackson

Где взять: здесь / репозиторий на 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

JsonPath

Где взять: через 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
READ ALSO
Как поменять ориентацию RecyclerView во время выполнения

Как поменять ориентацию RecyclerView во время выполнения

Пытаюсь изменить ориентацию на recyclerview во время выполнения по событию onScrolledНиже привожу метод onCreate():

288
Проверка строки содержащей число

Проверка строки содержащей число

Имеется 3 textField, в которые нужно вводить только целые или дробные числаПо нажатию кнопки произвожу вычисления с числами, вытянутыми из textField'ов,...

289
Таймер JAVA android

Таймер JAVA android

Вопрос такой: Нужно сделать так, чтобы int раз в 5 секунд меняла своё значение(В начале 0, через 5 секунд - 1, 1 на одну секунду, потом опять 0 и так...

255
Поддержка Java в VS Code

Поддержка Java в VS Code

Здравствуйте! В общем пытаюсь настроить VS Code для компиляции java-файловУстановил расширения:

314