Отображение объекта JSON в Java с использованием Jackson, когда внутренний объект json является условно пустым - PullRequest
0 голосов
/ 07 марта 2019

Я внедряю REST-клиент для открытого API-интерфейса musixmatch.com (который ищет слова на основе некоторых параметров, например, названия песни, исполнителя и т. Д.). (Всего новичок в REST / JSON).

Код моего клиента:

import java.io.IOException;
import java.io.InputStream;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.MappingJsonFactory;

import in.soumav.musixmatch.beans.MMResponse;

public class MusixMatchClient {

    public static void main(String[] args) throws JsonParseException, IOException {
        Client client = ClientBuilder.newClient();
        WebTarget target = client.target("https://api.musixmatch.com").path("ws").path("1.1")
                                        .path("matcher.lyrics.get")
                                        .queryParam("q_track", "Wake Me Up")
                                        .queryParam("q_artist", "Greenday")
                                        .queryParam("apikey", "...my key...");
        Builder builderRequest = target.request();
        Response response = builderRequest.get();
        if (Response.Status.OK.getStatusCode() == response.getStatus()) {
           MappingJsonFactory factory = new MappingJsonFactory();
           JsonParser parser = factory.createJsonParser((InputStream) response.getEntity());
           MMResponse resp = parser.readValueAs(MMResponse.class);
           System.out.println(resp.getMessage().getBody().getLyrics().getLyrics_body());
       }

    }
}

Анализатор JSON выполняет синтаксический анализ при обнаружении совпадения.

Ответ от API (в POSTMAN) при обнаружении совпадения:

{
    "message": {
        "header": {
            "status_code": 200,
            "execute_time": 0.076157093048096
        },
        "body": {
            "lyrics": {
                "lyrics_id": 14080789,
                "explicit": 0,
                "lyrics_body": "Summer has come and passed\nThe innocent can never last\nWake me up when September ends\n\nLike my father's come to pass\nSeven years has gone so fast\nWake me up when September ends\n\nHere comes the rain again\nFalling from the stars\nDrenched in my pain again\nBecoming who we are\n\nAs my memory rests\nBut never forgets what I lost\n...\n\n******* This Lyrics is NOT for Commercial use *******",
                "script_tracking_url": "https://tracking.musixmatch.com/t1.0/m_js/e_1/sn_0/l_14080789/su_0/rs_0/tr_3vUCADW8Npey61GB2HRIObKY4Sz3low2ucLdqisdi7cAswHSwWeSM5uhan_JCxhFMVX0wKQjr_6eciPf6twsVnnD4RPIGl8wpzGhgPLQRIkUkBP0Zga626PhvTK603MycNGrL9kMxuLM8iA8b8IYiLDV_WzIffNXs2ENvhz39AuqdldwE0H-mS51SpVUU21V--VBwXN1uBD3ra2GANeMeuvOMMP4-8sa1tE3FvaEVUaP-mqxgQtDJHG2_aY01nRNfzLPZ86xPscgeQMrPiILn5lpC7mkagXpQDqXo0MpoTnHZPFKqhrgiVNxkpZzX-oqam8DZflIgIe4zMtg2Y3QE8MvjE5vp500IZUOz-Q0GhVKfND5T6Yv3-jOuvkRkSxYbI8tJmRhbVe5XX3DATCdqAB7zYHrwyijL90yRJJzv2f-SC4E6f0J/",
                "pixel_tracking_url": "https://tracking.musixmatch.com/t1.0/m_img/e_1/sn_0/l_14080789/su_0/rs_0/tr_3vUCAIcjYkNNhw02X2Dtvu6SSWbscstTuW8YKvMwxRjtwZRmYXk4v0xsOdvjifHmsY2VY5yQTLHPUONXovxMI4XXZKXkC4KwjUwXR1afVXX5JE010OvQ-IsmsFkZ7-wXEXWJEFH5WJoKALES1HdQPDGsDNy6J5mGQURtG9MsQTSnfRnNz6zJkWTLZZ2F1GnG9f7ncW8guku-lOOWYAPcs8--4U4A3uR47_hn6PBh-JvHaQ2lFUjD6L0JuKs_Bgx0nU6RN97H02EkD6xkYWtsjZytPEneUHf3IrikZMu-tWvoclj0imoww8c_8NlqvHXG8pHYFO4YLPwk5kqfRXlW2TtwmJ9u1Do048_UFyCLAIctEEZBJEfcwjSep7VwU3BOiaKhL2Cwe-xV-NdW5pSzAbRb8bZ5PRrUnD7P-7SzdDSbossTyt6O/",
                "lyrics_copyright": "Lyrics powered by www.musixmatch.com. This Lyrics is NOT for Commercial use and only 30% of the lyrics are returned.",
                "updated_time": "2016-02-06T23:07:22Z"
            }
        }
    }
}

Однако, если совпадение не найдено, возвращается следующий ответ (при тестировании через POSTMAN):

{"message":{"header":{"status_code":404,"execute_time":0.11416816711426},"body":[]}}

Фактический код состояния в POSTMAN возвращает 200 OK хотя! А поскольку тело равно пустому массиву, анализатор JSON не может его проанализировать и выдает следующее исключение:

Исключение в теме "главная" org.codehaus.jackson.map.JsonMappingException: не может десериализовать экземпляр in.soumav.musixmatch.beans.Body из токена START_ARRAY в [Источник: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@d9345cd; строка: 1, столбец: 74] (через цепочку ссылок: in.soumav.musixmatch.beans.MMResponse [ "сообщение"] -> in.soumav.musixmatch.beans.Message [ "тело"])

У меня есть мои сопоставления объектов следующим образом:

             MMResponse 
                 |
              Message
           ______|____________________
          |                           | 
         Header(status_cd,           Body
       execution_time)                |
                                      |
                                    Lyrics(lyrics_id,lyrics_body etc.)

ВОПРОСЫ:

  1. Что было бы лучшим способом / подходом для достижения этого?
  2. Если мне придется вносить изменения в существующий код, как бы я его обработал?
  3. Как прочитать код_состояния в заголовке (в ответе JSON), прежде чем даже сопоставить его с объектом Java?

1 Ответ

2 голосов
/ 07 марта 2019

Кажется, что код состояния, который вы получаете от API, не соответствует коду состояния в объекте ответа.Поскольку этот ответ не должен удовлетворять вашему условию if Response.Status.OK.getStatusCode() == response.getStatus()

{"message":{"header":{"status_code":404,"execute_time":0.11416816711426},"body":[]}}

Если API неверен и вы хотите его обойти, вам необходимо проверить status_code из ответа JSON.Я хотел бы предложить что-то вроде:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;

...

if (Response.Status.OK.getStatusCode() == response.getStatus()) {
  ObjectMapper mapper = new ObjectMapper();
  // Validate object status status_code or message.header.status_code
  String jsonString = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());

  JsonNode jsonObject = mapper.readTree(jsonString);
  String statusCode =
          jsonObject.get("message")
                  .get("header")
                  .get("status_code")
                  .toString();

  if(statusCode.equalsIgnoreCase("200")) {
    MappingJsonFactory factory = new MappingJsonFactory();
    JsonParser parser = factory.createJsonParser((InputStream) response.getEntity());
    MMResponse resp = parser.readValueAs(MMResponse.class);
    System.out.println(resp.getMessage().getBody().getLyrics().getLyrics_body());  
  }
}

Выше реализации использовать Jackson Object Mapper и общие IOUtils.IOUtils можно найти в следующей зависимости:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.2</version>
</dependency>
...