Home > front end >  creates java objects from Json file got from API
creates java objects from Json file got from API

Time:10-04

I want to create a YuGiOh! game software. So, the first step for me was collecting all cards to make a card search engine.

So I called an API to get cards data, and the issue I don't to how from it map them into card class and add them to a card list the will be displayed to the user who will select the ones he needs.

THANK YOU FOR YOUR HELP

    package card;

import java.util.List;

public class card {
    private int atk;
    private int def;
    private int level;
    private String race;
    private String attribute;
    private String archetype;
    private String imagePath;
    private String name;

    card(int atk, int def, int level, String name, String attribute, String imagePath)
    {
        this.atk = atk;
        this.def = def;
        this.level = level;
        this.attribute = attribute;
        this.imagePath = imagePath;
        this.name = name;
    }

    public int getAtk() {
        return atk;
    }

    public void setAtk(int atk) {
        this.atk = atk;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public String getAttribute() {
        return attribute;
    }

    public void setAttribute(String attribute) {
        this.attribute = attribute;
    }

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String imagePath) {
        this.name= name;
    }
}
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import card.card;

public class API {
    
    public void searchCard()
    {
        try 
        {

             URL url = new URL("https://db.ygoprodeck.com/api/v7/cardinfo.php?name=Dark%");
    
             HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             
             conn.setRequestMethod("GET");
             
             conn.connect();
    
             //Check if connect is made
             int responseCode = conn.getResponseCode();
    
             // 200 OK
             if (responseCode != 200) 
             {
                 throw new RuntimeException("HttpResponseCode: "   responseCode);
             } 
             else 
             {
                
                 ObjectMapper mapper = new ObjectMapper();
                 JsonNode cardData = mapper.readTree(url.openStream());
                 card[] cardList =  mapper.readValue(url.openStream(), card[].class);
                 System.out.print(cardList[0].getName());
                 //return cardData;
             }
         } 
        catch (Exception e) 
        {
             e.printStackTrace();
         }
    }
}
    com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `[Lcard.card;` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: (sun.net.www.protocol.http.HttpURLConnection$HttpInputStream); line: 1, column: 1]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1746)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1520)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1467)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.handleNonArray(ObjectArrayDeserializer.java:345)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:196)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:26)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4697)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3689)
    at API.API.searchCard(API.java:46)
    at APP.app.main(app.java:9)

Here is an example of data collected from the api.

{"data":[{"id":55289183,"name":"Capricious Darklord","type":"Effect Monster","desc":"During the Main Phase (Quick Effect): You can activate this effect; Tribute Summon 1 Fairy monster face-up. If this card is sent to the GY: You can make all monsters your opponent currently controls lose 500 ATK/DEF for each Fairy monster on the field, until the end of this turn. You can only use each effect of \"Capricious Darklord\" once per turn.","atk":0,"def":1600,"level":4,"race":"Fairy","attribute":"DARK","archetype":"Darklord","card_sets":[{"set_name":"2021 Tin of Ancient Battles","set_code":"MP21-EN117","set_rarity":"Common","set_rarity_code":"(C)","set_price":"0.88"},{"set_name":"Rise of the Duelist","set_code":"ROTD-EN023","set_rarity":"Common","set_rarity_code":"(C)","set_price":"0.99"}],"card_images":[{"id":55289183,"image_url":"https://storage.googleapis.com/ygoprodeck.com/pics/55289183.jpg","image_url_small":"https://storage.googleapis.com/ygoprodeck.com/pics_small/55289183.jpg"}],"card_prices":[{"cardmarket_price":"0.08","tcgplayer_price":"0.08","ebay_price":"0.99","amazon_price":"0.25","coolstuffinc_price":"0.49"}]},{"id":85325774,"name":"Charge Into a Dark World","type":"Spell Card","desc":"Target 1 Level 4 or lower Fiend monster in your GY; Special Summon it, then discard 1 Fiend monster. You can only activate 1 \"Charge Into a Dark World\" per turn.","race":"Normal","archetype":"Dark World","card_sets":[{"set_name":"2021 Tin of Ancient Battles","set_code":"MP21-EN206","set_rarity":"Common","set_rarity_code":"(C)","set_price":"0.96"},{"set_name":"Phantom Rage","set_code":"PHRA-EN063","set_rarity":"Common","set_rarity_code":"(C)","set_price":"0.99"}],"card_images":...

CodePudding user response:

There are a number of problems you have here.

The first problem you have is that you are trying to deserialize a Java array from an JSON object. You can't do that: you can only deserialize a JSON object into an instance of a Java class, or a JSON array into a Java array (or List)

The JSON you are trying to deserialize looks like this:

{
    "data": [
        {
            "id": 55289183,
            "name": "Capricious Darklord",
    ...
}

However, in your line

        card[] cardList =  mapper.readValue(url.openStream(), card[].class);

you are trying to deserialize it as if it looked like the following:

[
    {
        "id": 55289183,
        "name": "Capricious Darklord",
    ...
]

What you'll need to do is to write a small Java class that has a data property, and deserialize your data into an instance of that. This class would look something like the following:

public class CardList {
    
    private card[] data;

    public card[] getData() {
        return data;
    }

    public void setData(card[] data) {
        this.data = data;
    }
}

Then adjust the code that deserializes your JSON to the following:

        CardList cardList = mapper.readValue(url.openStream(), CardList.class);
        System.out.print(cardList.getData()[0].getName());

The next problem you will have is that your JSON data contains properties that aren't in your card class, such as id, type and desc. Jackson will complain that it doesn't know what to do with data for properties that it can't find in your Java class, but you can tell it to ignore these properties by adding the line

        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

immediately after the one in which you create an ObjectMapper. You might also need to add the following line:

import com.fasterxml.jackson.databind.DeserializationFeature;

The next error you may get is Cannot construct instance of 'com.example.card' (no Creators, like default constructor, exist). The simplest way to get rid of this is to remove the constructor in the card class: Jackson will then create card objects by calling the auto-generated zero-argument constructor.

After making all of these changes, you should find your code runs and doesn't throw an exception, but instead it prints out null. But why has it done this? Take a closer look at the setName setter in your card class:

    public void setName(String imagePath) {
        this.name= name;
    }

Note that the name of the method parameter is imagePath. This should be name instead. Because the method parameter has the wrong name, the value passed in to this method is ignored, and the line this.name= name ends up doing this.name = this.name, which isn't very useful. Note that IDEs such as Eclipse, NetBeans and IntelliJ IDEA can generate getters and setters for you, which avoids mistakes like this.

I made these changes to your code and it was able to deserialize your JSON data and have the program print out the name of the first card.

CodePudding user response:

The error cannot deserialize value of type [Lcard.card; from Object value

means that Jackson cannot deserialize a card Array from a JSON value that isn't an array but an object.

The JSON object it encounters is

{
   "data": [ {card}, {card}, {card} ]
}

so you have to map to an Object that contains a list/array of cards in the data field. For that you can for example create a class like

class Response {
    public data: card[]
}

Then ask Jackson to deserialize that and later access the cards via the data field.


[Lcard by the means: [ it's an array type, and the Lcard means there's a class (L) named card. I.e. card[].

It's also by the way common to start class names UpperCase. Would recommend you stick to that or people used to Java get confused by your code.

If you don't want to write all those Java classes youself, you can by the way find numerous JSON to Java / Pojo generators (e.g. https://www.jsonschema2pojo.org/) on the internet, They can generate the classes for you and all you need to do is copy them into your sourcecode. Very nice for large replies like this.

  • Related