Cookies op Tweakers

Tweakers maakt gebruik van cookies, onder andere om de website te analyseren, het gebruiksgemak te vergroten en advertenties te tonen. Door gebruik te maken van deze website, of door op 'Ga verder' te klikken, geef je toestemming voor het gebruik van cookies. Wil je meer informatie over cookies en hoe ze worden gebruikt, bekijk dan ons cookiebeleid.

Meer informatie
Toon posts:

[Java] JSON processing met GSON

Pagina: 1
Acties:

Vraag


Acties:
  • 0Henk 'm!

  • Croga
  • Registratie: oktober 2001
  • Laatst online: 18:06

Croga

The Unreasonable Man

Topicstarter
Mijn vraag

Om mijn Developer kennis weer een beetje up to date te brengen ben ik voor mezelf een tooltje aan het bouwen ter ondersteuning van een bordspel.

Het gaat om een bordspel waarbij ruimteschepen elkaar beschieten. De wapens hebben een tabel waarbij aan de hand van de afstand tot het doel en een dobbelsteenworp de schade wordt bepaald. Een wapen kan meerdere "Firing modes" hebben. Een wapen heeft dan ook verschillende firing modes die ieder afhankelijk van afstand en een dobbelsteenworp een getal aan schade hebben.

De data hiervoor heb ik in een JSON bestand staan. Het gaat om nogal een aantal verschillende wapens en firingmodes dus een bestand als input is gewenst (wellicht dat dat ooit nog refactored gaat worden naar een database table, voor het moment niet nodig).

De JSON ziet er ongeveer zo uit (alleen dan met een hele berg wapens en firing modes :-))
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    "weapons": [
        { "name": "disruptor",
        "firing modes": [
            { "name": "standard",
            "range table": [
                ["0","0"],
                ["1","1"],
                ["2","2"],
                ["3","4"],
                ["5","15"],
                ["16","25"]
            ],
            "damage table": [
                ["5", "5", "5", "5", "5", "5"],
                ["5", "5", "5", "5", "5", "0"],
                ["4", "4", "4", "4", "4", "0"],
                ["4", "4", "4", "4", "0", "0"],
                ["3", "3", "3", "3", "0", "0"],
                ["2", "2", "2", "0", "0", "0"]
            ] },
            { "name": "overload",
            "range table": [
                ["0","0"],
                ["1","1"],
                ["2","2"],
                ["3","4"],
                ["5","8"]
            ],
            "damage table": [
                ["10", "10", "10", "10", "10", "10"],
                ["10", "10", "10", "10", "10", "0"],
                ["8", "8", "8", "8", "8", "0"],
                ["8", "8", "8", "8", "0", "0"],
                ["6", "6", "6", "6", "0", "0"]
            ] }
        ] }
    ]
}


In PHP is het verwerken van die JSON eenvoudig. De JSON parser maakt er een mooie multilayered multidimensionele named array van. Ik zeg: Geef mij de array die onder "weapons" hangt en itereer daar doorheen. Itereer vervolgens over de array die onder "firing modes" hangt. etc.

Mijn zoektocht vertelt mij dat GSON the way to go is wanneer ik JSON wil verwerken in Java. GSON geeft mij de optie:
code:
1
Map map = gson.fromJson(jsonString, Map.class);

die iets soortgelijks zou moeten opleveren.
De grote uitdaging is dat Java iets kritischer is met types. Simpelweg zeggen "Geef mij alles wat onder "weapons" hangt" is dus geen optie; ik moet hem vertellen wat daar onder hangt. Het probleem is alleen dat de code helemaal niet weet wat er onder hangt aangezien dat pas vanuit de file mee komt.

Ik weet dus dat er een Map<String, Map> onder zal hangen maar ik kan niet zeggen:
code:
1
for (Map<String, Map> weaponAsMap : weaponsAsMap.entrySet()) {

omdat de entrySet slechts objecten terug geeft en geen Maps. Nou zou ik weaponsAsMap als Object kunnen definieren maar dan zal het in de volgende stap fout gaan.


Is er een betere manier om door een geïmporteerd JSON map heen te lopen? Of mischien zelfs een betere manier om überhaupt die JSON binnen te halen?

Het is één ding om googlefu of stackoverflowfu te gebruiken voor generieke zaken maar nergens iets te vinden over JSON die iets ingewikkelder in elkaar steekt :-D

Beste antwoord (via Croga op 18-05-2020 12:04)


  • Sleepkever
  • Registratie: juni 2007
  • Laatst online: 16:18
Je moet het ook niet als een map zien maar als meerdere objecten, {} in javascript definieert over het algemeen ook een object en geen map. Objecten zijn in Java wel een stuk vaster dan het dynamische javascript dus zal je de inhoud van die objecten netjes vast moeten leggen in een class. Dat gebeurt normaal in de vorm van een POJO, een plain old java object zonder verdere logica.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Weapons {
    //dit is je eerste object met "weapons" die een lijst van subobjecten bevat
    private List<Weapon> weapons; 
    
    //getters/setters
}

public class Weapon {
    private String name;
    private List<FiringMode> firing_modes 
    //Geen idee hoe gson met spaties in namen om gaat, zou je even uit moeten zoeken. 
    //Kan je waarschijnlijk oplossen met een instelling, een annotatie boven elk veld (@SerializedName) of gewoon renamen in de bron.

   //getters/setters
}

public class FiringMode {
    private String name;
    private List<Integer> range_table;
    private List<Integer> damage_table;
    
    //getters/setters
}


Vervolgens kan je dan de hele bak parsen met
Java:
1
Weapons weapons = gson.fromJson(jsonString, Weapons.class);

Waarbij gson dan het mappen van de objecten voor zijn rekening neemt. Zo'n vaste structuur maakt het ook een stuk makkelijker om in je code er mee te werken en je hebt niet overal meer strings nodig om toegang te krijgen tot keys.

[Voor 0% gewijzigd door Sleepkever op 18-05-2020 11:46. Reden: private-ed when i should have public-ed]

Alle reacties


Acties:
  • Beste antwoord
  • +1Henk 'm!

  • Sleepkever
  • Registratie: juni 2007
  • Laatst online: 16:18
Je moet het ook niet als een map zien maar als meerdere objecten, {} in javascript definieert over het algemeen ook een object en geen map. Objecten zijn in Java wel een stuk vaster dan het dynamische javascript dus zal je de inhoud van die objecten netjes vast moeten leggen in een class. Dat gebeurt normaal in de vorm van een POJO, een plain old java object zonder verdere logica.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Weapons {
    //dit is je eerste object met "weapons" die een lijst van subobjecten bevat
    private List<Weapon> weapons; 
    
    //getters/setters
}

public class Weapon {
    private String name;
    private List<FiringMode> firing_modes 
    //Geen idee hoe gson met spaties in namen om gaat, zou je even uit moeten zoeken. 
    //Kan je waarschijnlijk oplossen met een instelling, een annotatie boven elk veld (@SerializedName) of gewoon renamen in de bron.

   //getters/setters
}

public class FiringMode {
    private String name;
    private List<Integer> range_table;
    private List<Integer> damage_table;
    
    //getters/setters
}


Vervolgens kan je dan de hele bak parsen met
Java:
1
Weapons weapons = gson.fromJson(jsonString, Weapons.class);

Waarbij gson dan het mappen van de objecten voor zijn rekening neemt. Zo'n vaste structuur maakt het ook een stuk makkelijker om in je code er mee te werken en je hebt niet overal meer strings nodig om toegang te krijgen tot keys.

[Voor 0% gewijzigd door Sleepkever op 18-05-2020 11:46. Reden: private-ed when i should have public-ed]


Acties:
  • +1Henk 'm!

  • Giesber
  • Registratie: juni 2005
  • Laatst online: 11:13
De library Jackson heb ik daar al voor gebruikt.

Wat zou moeten kunnen is dat je je klasses definieert: Weapon met een String "name"; een list "firingModes" van FiringMode, waarop je dan nog een annotatie moet zetten dat "firingModes" eigenlijk "firing modes" heet in de JSON: @JsonProperty("value").

Vervolgens FiringMode ook definieren, je hebt het principe al wel begrepen denk ik.

Edit: Wat Sleepkever dus veel mooier laat zien :) .

[Voor 5% gewijzigd door Giesber op 18-05-2020 11:48]


Acties:
  • +2Henk 'm!

  • Merethil
  • Registratie: december 2008
  • Laatst online: 18:18
Sleepkever schreef op maandag 18 mei 2020 @ 11:44:
Je moet het ook niet als een map zien maar als meerdere objecten, {} in javascript definieert over het algemeen ook een object en geen map. Objecten zijn in Java wel een stuk vaster dan het dynamische javascript dus zal je de inhoud van die objecten netjes vast moeten leggen in een class. Dat gebeurt normaal in de vorm van een POJO, een plain old java object zonder verdere logica.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Weapons {
    //dit is je eerste object met "weapons" die een lijst van subobjecten bevat
    private List<Weapon> weapons; 
    
    //getters/setters
}

public class Weapon {
    private String name;
    private List<FiringMode> firing_modes 
    //Geen idee hoe gson met spaties in namen om gaat, zou je even uit moeten zoeken. 
    //Kan je waarschijnlijk oplossen met een instelling, een annotatie boven elk veld (@SerializedName) of gewoon renamen in de bron.

   //getters/setters
}

public class FiringMode {
    private String name;
    private List<Integer> range_table;
    private List<Integer> damage_table;
    
    //getters/setters
}


Vervolgens kan je dan de hele bak parsen met
Java:
1
Weapons weapons = gson.fromJson(jsonString, Weapons.class);

Waarbij gson dan het mappen van de objecten voor zijn rekening neemt. Zo'n vaste structuur maakt het ook een stuk makkelijker om in je code er mee te werken en je hebt niet overal meer strings nodig om toegang te krijgen tot keys.
Klinkt op zich goed, ik zie alleen dat range_table en damage_table een lijst van lijsten zijn. Lijkt mij dus meer een
Java:
1
2
    private List<List<Integer>> range_table;
    private List<List<Integer>> damage_table;


Of als ze unieke lijsten moeten bevatten:
Java:
1
2
    private Set<List<Integer>> range_table;
    private Set<List<Integer>> damage_table;


Maar dat is heel erg afhankelijk van het hele nut van die lijsten... Ik zou niet weten wat die data exact betekent; je zou denken dat er één range en één damage is, en dat die eventueel af kan hangen van elkaar. Waarom je zo'n hele lijst aan ranges/damages "per lijst" hebt snap ik niet. Maar dat is aan de topic starter om te bepalen :P

Acties:
  • 0Henk 'm!

  • Croga
  • Registratie: oktober 2001
  • Laatst online: 18:06

Croga

The Unreasonable Man

Topicstarter
Merethil schreef op maandag 18 mei 2020 @ 11:49:
Maar dat is heel erg afhankelijk van het hele nut van die lijsten... Ik zou niet weten wat die data exact betekent; je zou denken dat er één range en één damage is, en dat die eventueel af kan hangen van elkaar. Waarom je zo'n hele lijst aan ranges/damages "per lijst" hebt snap ik niet. Maar dat is aan de topic starter om te bepalen :P
Range is een bereik. Range (ofwel: Afstand tot doelwit) wordt in hexes gegeven maar is een bereik. Dus een Standaard Disruptor vuurt op range 0, 1, 2, 3 tot 4, 5 tot 15, 16 tot 25.
Damage is een die roll. Damage Table bevat dus 6 entries; 1 per die roll.

Ofwel: Als ik een Standaard Disruptor afvuur op Range 6 en ik rol een 3 dan zie ik aan RangeTable dat ik in de 5e kolom zit. In de 5e kolom van de DamageTable zie ik vervolgens dat entry 3 een 3 is voor 3 punten schade.

Afijn; bedankt voor al jullie input! Ik ga eens wat verder hobbyen!
Sleepkever schreef op maandag 18 mei 2020 @ 11:44:
Vervolgens kan je dan de hele bak parsen met
Java:
1
Weapons weapons = gson.fromJson(jsonString, Weapons.class);

Waarbij gson dan het mappen van de objecten voor zijn rekening neemt. Zo'n vaste structuur maakt het ook een stuk makkelijker om in je code er mee te werken en je hebt niet overal meer strings nodig om toegang te krijgen tot keys.
*bleep*dammit hoe makkelijk kan het zijn.... En ik maar denken dat dat niet gaat lukken vanwege de nesting.

[Voor 30% gewijzigd door Croga op 18-05-2020 12:08]


Acties:
  • 0Henk 'm!

  • Croga
  • Registratie: oktober 2001
  • Laatst online: 18:06

Croga

The Unreasonable Man

Topicstarter
Merethil schreef op maandag 18 mei 2020 @ 11:49:
Klinkt op zich goed, ik zie alleen dat range_table en damage_table een lijst van lijsten zijn. Lijkt mij dus meer een
Java:
1
2
    private List<List<Integer>> range_table;
    private List<List<Integer>> damage_table;
In de categorie wellicht domme vragen; kan ik hier ook een int[][] uit laten komen in plaats van een List<List<int>> ? Of is er een manier om bij het laden van de JSON deze omzetting al te doen?

Acties:
  • +1Henk 'm!

  • Merethil
  • Registratie: december 2008
  • Laatst online: 18:18
Croga schreef op maandag 18 mei 2020 @ 14:42:
[...]


In de categorie wellicht domme vragen; kan ik hier ook een int[][] uit laten komen in plaats van een List<List<int>> ? Of is er een manier om bij het laden van de JSON deze omzetting al te doen?
Het lijkt mij een kwestie van proberen maar ik zou zeggen van wel. Ik weet niet of GSON hier goed mee overweg kan, zelf gebruik ik meestal Jackson en die moet je soms wat hints geven wil 'ie het goed kunnen.

Acties:
  • +1Henk 'm!

  • Sleepkever
  • Registratie: juni 2007
  • Laatst online: 16:18
Croga schreef op maandag 18 mei 2020 @ 14:42:
[...]


In de categorie wellicht domme vragen; kan ik hier ook een int[][] uit laten komen in plaats van een List<List<int>> ? Of is er een manier om bij het laden van de JSON deze omzetting al te doen?
Oei, dat is waarschijnlijk best tricky. Ik heb niet zoveel ervaring met GSON maar als ik zou moeten gokken zit daar niet default support in voor GSON.

In Java is het heel gebruikelijk om ArrayList te gebruiken ipv de primitive arrays. Primitive arrays zijn namelijk fixed size in Java en een stuk minder flexibel. Dingen toevoegen of verwijderen in primitive arrays betekend echt dat je ze compleet opnieuw op moet bouwen. Tenzij je echt tegen memory limieten aan gaat lopen zou ik je adviseren om gewoon ArrayList's te gebruiken.

Acties:
  • 0Henk 'm!

  • Croga
  • Registratie: oktober 2001
  • Laatst online: 18:06

Croga

The Unreasonable Man

Topicstarter
Sleepkever schreef op maandag 18 mei 2020 @ 15:43:
Oei, dat is waarschijnlijk best tricky. Ik heb niet zoveel ervaring met GSON maar als ik zou moeten gokken zit daar niet default support in voor GSON.
Ik heb gewoon m'n declaraties aangepast, unit tests gedraaid en blijkbaar werkt het :-D
In Java is het heel gebruikelijk om ArrayList te gebruiken ipv de primitive arrays. Primitive arrays zijn namelijk fixed size in Java en een stuk minder flexibel. Dingen toevoegen of verwijderen in primitive arrays betekend echt dat je ze compleet opnieuw op moet bouwen. Tenzij je echt tegen memory limieten aan gaat lopen zou ik je adviseren om gewoon ArrayList's te gebruiken.
De truuk is dat deze arrays vanuit de JSON komen en daarna niet meer mogen veranderen.
Het voordeel van het gebruik van primitives is dat het in dit geval eenvoudiger is om mee te werken. Ik kan een afstand naar het doelwit aangeven, die opzoeken in de range tabel en de index van die entry op de damage tabel los laten om de 6 mogelijke dobbelsteen worpen te krijgen. Lists zijn daar een stuk lastiger in.

Mensen allemaal hartelijk dank! Ik ben weer een paar meter verder!

Acties:
  • +1Henk 'm!

  • Sleepkever
  • Registratie: juni 2007
  • Laatst online: 16:18
Croga schreef op maandag 18 mei 2020 @ 15:49:
[...]

Ik heb gewoon m'n declaraties aangepast, unit tests gedraaid en blijkbaar werkt het :-D


[...]

De truuk is dat deze arrays vanuit de JSON komen en daarna niet meer mogen veranderen.
Het voordeel van het gebruik van primitives is dat het in dit geval eenvoudiger is om mee te werken. Ik kan een afstand naar het doelwit aangeven, die opzoeken in de range tabel en de index van die entry op de damage tabel los laten om de 6 mogelijke dobbelsteen worpen te krijgen. Lists zijn daar een stuk lastiger in.

Mensen allemaal hartelijk dank! Ik ben weer een paar meter verder!
Cool! Goed om te weten! En met die usecase kan ik me inderdaad voorstellen dat fixed immutable arrays dan juist een voordeel zijn.
Pagina: 1


Apple iPhone 12 Microsoft Xbox Series X LG CX Google Pixel 5 Black Friday 2020 Samsung Galaxy S20 4G Sony PlayStation 5 Nintendo Switch Lite

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2020 Hosting door True