Я пытаюсь написать следующий объект Player как String, используя ObjectMapper от Jackson.
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: Invalid type definition for type
com.sun.javafx.scene.NodeEventDispatcher: Failed to construct BeanSerializer for [simple type, class com.sun.javafx.scene.NodeEventDispatcher]: (java.lang.reflect.InaccessibleObjectException) Unable to make public final com.sun.javafx.event.BasicEventDispatcher com.sun.javafx.event.BasicEventDispatcher.getPreviousDispatcher() accessible: module javafx.base does not "exports com.sun.javafx.event" to module com.fasterxml.jackson.databind (through reference chain: models.communication.Websockets.ConnectionSubmitModel["player"]->models.Game.Player["circle"]->javafx.scene.shape.Circle["parent"]->javafx.scene.layout.GridPane["parent"]->javafx.scene.layout.AnchorPane["eventDispatcher"])
Как говорится в ошибке, это как-то связано с тем, что JavaFx не экспортирует определенную зависимость, но, поскольку я не контролирую JavaFx, я не совсем уверен, как это исправить.




Вы пытаетесь сохранить класс Circle, который является классом JavaFX, который на самом деле не является классом данных (это элемент пользовательского интерфейса) со многими свойствами (такими как радиус, толщина, цвет, заливка, границы и т. д.). Таким образом, он по-разному связан с системой JavaFX и не будет хорошо храниться.
Вместо этого просто сохраните нужную информацию в собственном простом классе, в котором есть информация, необходимая для повторного создания объекта Circle, когда вы его читаете.
Класс Color может дать ту же проблему. Кроме того, вы должны использовать @JsonIgnore в геттере вместо transient, потому что Джексон по умолчанию смотрит на геттеры, чтобы определить, что сериализовать (вы также можете настроить его для просмотра полей, если хотите... В этом случае он может уважать transient ). См. этот ответ для получения дополнительной информации об этом: stackoverflow.com/questions/9112900/…
Как правило, 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 есть побочный эффект. Он убирает жизнь — что бы это ни значило. Как правило, это плохая практика, но этот вопрос не об именовании.
Хорошо подумав об этом, мне даже не нужно сериализовать объект Circle, но после прочтения этого добавление ключевого слова «transient», похоже, не помогает