Java InvalidDefinitionException при сериализации объекта с привязкой данных Джексона - PullRequest
4 голосов
/ 25 мая 2019

Я пытаюсь записать следующий объект Player как String, используя ObjectMapper от Джексона.

package models.Game;

import models.Game.Enums.SnowballState;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

import java.util.ArrayList;
import java.util.List;

public class Player {
    private Circle circle;
    private String name;
    private Color color;
    private int points = 0;
public int getLives() {
    return lives;
}

private int lives = 3;
private List<Snowball> snowballs;
private Circle oldCircle;
private int stepSize = 10;

public Player(String name, Color color) {
    this.name = name;
    circle = new Circle();
    oldCircle = new Circle();
    this.color = color;
    snowballs = new ArrayList<>();
    snowballs.add(new Snowball(this));
    snowballs.add(new Snowball(this));
    snowballs.add(new Snowball(this));
}

public Player() {

}

private void removeLife() {
    this.lives--;
}

public int getHit() {
    removeLife();
    return getLives();
}

public int shotSuccess() {
    points+= 50;
    return points;
}

public int getSnowballAmount() {
    int balls = 0;
    for (Snowball ball : snowballs) {
        if (ball.getState() == SnowballState.CREATED) {
            balls++;
        }
    }
    return balls;
}

public List<Snowball> getSnowballs() {
    return snowballs;
}

public Snowball getNextSnowball() {
    for (Snowball ball : snowballs) {
        if (ball.getState() == SnowballState.CREATED) {
            return ball;
        }
    }
    return null;
}

public void createSnowball() {
    if (getSnowballAmount() < 3) {
        snowballs.add(new Snowball(this));
    }
}

public Color getColor() {
    return this.color;
}

public Circle getCircle() {
    return this.circle;
}

public void moveLeft() {
    saveOld();
    circle.setTranslateX(circle.getTranslateX() - stepSize);
}

public void moveRight() {
    saveOld();
    circle.setTranslateX(circle.getTranslateX() + stepSize);
}

public void moveUp() {
    saveOld();
    circle.setTranslateY(circle.getTranslateY() - stepSize);
}

public void moveDown() {
    saveOld();
    circle.setTranslateY(circle.getTranslateY() + stepSize);
}

public void undo() {
    circle.setTranslateX(oldCircle.getTranslateX());
    circle.setTranslateY(oldCircle.getTranslateY());
}

private void saveOld() {
    oldCircle.setTranslateX(circle.getTranslateX());
    oldCircle.setTranslateY(circle.getTranslateY());
}

public Snowball shootSnowball(Snowball ball, double mouseX, double mouseY) {

    double polarDirection = Math.atan2(mouseY - circle.getTranslateY(), mouseX - circle.getTranslateX() + 50);
    ball.setState(SnowballState.ALIVE);
    ball.setDirection(polarDirection);
    ball.getCircle().setTranslateX(circle.getTranslateX() + 50);
    ball.getCircle().setTranslateY(circle.getTranslateY());
    return ball;
}

Я использую следующую команду для этого:

 String json = null;
        try {
            json = objectMapper.writeValueAsString(instanceOfPlayerClass);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

к сожалению, я получаю следующее соответствующее сообщение об ошибке:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Неверное определение типа для типа com.sun.javafx.scene.NodeEventDispatcher: Не удалось создать BeanSerializer для [простой тип, класс com.sun.javafx.scene.NodeEventDispatcher]: (java.lang. refle.InaccessibleObjectException) Невозможно сделать общедоступным окончательное com.sun.javafx.event.BasicEventDispatcher com.sun.javafx.event.BasicEventDispatcher.getPreviousDispatcher () доступным: модуль javafx.base не «экспортирует com.sun.javafx.event» модуль com.fasterxml.jackson.databind (через цепочку ссылок: models.communication.Websockets.ConnectionSubmitModel ["player"] -> models.Game.Player ["circle"] -> javafx.scene.shape.Circle ["parent" ] -> javafx.scene.layout.GridPane [ "родитель"] -> javafx.scene.layout.AnchorPane [ "EventDispatcher"])

Как и в сообщении об ошибке, это связано с тем, что JavaFx не экспортирует определенную зависимость, но, поскольку я не контролирую JavaFx, я не совсем уверен, как это исправить.

Ответы [ 2 ]

1 голос
/ 25 мая 2019

Обычно Jackson лучше всего работает с POJO классами. Если вы хотите сериализовать бизнес-объекты, может возникнуть много непредвиденных ошибок. Вероятно, лучшим решением было бы создание новых классов моделей, представляющих состояние из Player и Snowball. Что-то вроде PlayerState и SnowballState. Эти два класса должны следовать правилам POJO: getters, setters, no-arg constructor и т. Д. Когда вам нужно сохранить состояние в JSON, вы можете преобразовать свою бизнес-модель в модель состояния и сериализация модель состояния . Когда вам нужно десериализовать JSON, вам нужно десериализовать его в модель состояния и после этого преобразовать в бизнес-модель . Для классов JavaFX вам необходимо реализовать собственные сериализаторы и десериализаторы, если это необходимо. Они также не являются регулярными POJO классами и нуждаются в специальном лечении.

Позволяет реализовать два сериализатора и один десериализатор:

class CircleJsonSerializer extends JsonSerializer<Circle> {

    @Override
    public void serialize(Circle value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartObject();
        gen.writeNumberField("radius", value.getRadius());
        gen.writeNumberField("centerX", value.getCenterX());
        gen.writeNumberField("centerY", value.getCenterY());
        gen.writeEndObject();
    }
}

class CircleJsonDeserializer extends JsonDeserializer<Circle> {

    @Override
    public Circle deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        TreeNode node = p.readValueAsTree();
        NumericNode radius = (NumericNode) node.get("radius");
        NumericNode centerX = (NumericNode) node.get("centerX");
        NumericNode centerY = (NumericNode) node.get("centerY");

        return new Circle(centerX.doubleValue(), centerY.doubleValue(), radius.doubleValue());
    }
}

class ColorJsonDeserializer extends JsonDeserializer<Color> {
    @Override
    public Color deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        TreeNode node = p.readValueAsTree();
        NumericNode red = (NumericNode) node.get("red");
        NumericNode green = (NumericNode) node.get("green");
        NumericNode blue = (NumericNode) node.get("blue");
        NumericNode opacity = (NumericNode) node.get("opacity");

        return Color.color(red.doubleValue(), green.doubleValue(), blue.doubleValue(), opacity.doubleValue());
    }
}

Вы можете использовать их, как показано ниже:

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.NumericNode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        Player player = new Player("N1", Color.BLUE);

        SimpleModule javafxModule = new SimpleModule();
        javafxModule.addSerializer(Circle.class, new CircleJsonSerializer());
        javafxModule.addDeserializer(Circle.class, new CircleJsonDeserializer());
        javafxModule.addDeserializer(Color.class, new ColorJsonDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(javafxModule);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        String json = mapper.writeValueAsString(player);
        System.out.println(json);
        System.out.println(mapper.readValue(json, Player.class));
    }
}

Над отпечатками кодов:

{
  "circle" : {
    "radius" : 1.0,
    "centerX" : 0.0,
    "centerY" : 0.0
  },
  "color" : {
    "red" : 0.0,
    "green" : 0.0,
    "blue" : 1.0,
    "opacity" : 1.0,
    "opaque" : true,
    "hue" : 240.0,
    "saturation" : 1.0,
    "brightness" : 1.0
  },
  "lives" : 3,
  "snowballs" : [ {
    "state" : "CREATED",
    "direction" : 0.0,
    "circle" : null
  }, {
    "state" : "CREATED",
    "direction" : 0.0,
    "circle" : null
  }, {
    "state" : "CREATED",
    "direction" : 0.0,
    "circle" : null
  } ]
}

//ToString
Player{circle=Circle[centerX=0.0, centerY=0.0, radius=1.0, fill=0x000000ff], name='null', color=0x0000ffff, points=0, lives=3, snowballs=[Snowball{player=null, state=CREATED, direction=0.0, circle=null}, Snowball{player=null, state=CREATED, direction=0.0, circle=null}, Snowball{player=null, state=CREATED, direction=0.0, circle=null}], oldCircle=null, stepSize=10}

Как видите, мы можем сериализовать и десериализовать Player класс, но он требует много дополнительной работы. Также для каждого getter метода, который выполняет бизнес-логику, я игнорировал их, как показано ниже:

@JsonIgnore
public int getHit() {
    removeLife();
    return getLives();
}

Еще один совет: getHint метод имеет побочный эффект. Это удаляет жизнь - что бы это ни значило. Как правило, это плохая практика, но этот вопрос не касается имен.

1 голос
/ 25 мая 2019

Вы пытаетесь сохранить класс Circle, который является классом JavaFX, который на самом деле не является классом данных (это элемент пользовательского интерфейса), со многими свойствами (такими как радиус, толщина, цвет, заливка, границы так далее.). Как таковой он связан с системой JavaFX различными способами и не будет хорошо храниться.

Вместо этого просто сохраните нужную информацию в собственном простом классе, в котором есть информация, необходимая для повторного создания объекта Circle, когда вы читаете его обратно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...