..循环引用解决方案来自这里:JAXB注释-如何使XmlIDRef元素列表具有id值作为属性而不是元素正文?
下面是我的解决方案:
这是需要(un)编组的类:
package at.jku.buildingsimulator.model;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import at.jku.buildingsimulator.MainApp;
import at.jku.buildingsimulator.jaxb.ConnectionAdapter;
import at.jku.buildingsimulator.jaxb.SensorAdapter;
import at.jku.indoorpersontracker.model.Room;
import at.jku.indoorpersontracker.sensor.Sensor;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.collections.ObservableSet;
@XmlType(name = "RoomModel")
public class RoomModel {
@XmlType(name = "WallPosition")
public static enum WallPosition {
LEFT, RIGHT, TOP, BOTTOM, NOT_ADJACENT, DIFFERENT_FLOOR, OUTSIDE;
public static WallPosition opposite(WallPosition position) {
switch(position) {
case LEFT: return RIGHT;
case RIGHT: return LEFT;
case TOP: return BOTTOM;
case BOTTOM: return TOP;
case NOT_ADJACENT: return NOT_ADJACENT;
case DIFFERENT_FLOOR: return DIFFERENT_FLOOR;
case OUTSIDE: return OUTSIDE;
default: return DIFFERENT_FLOOR;
}
}
}
private IntegerProperty roomNumber = new SimpleIntegerProperty();
private ObservableSet<Sensor> sensors = FXCollections.observableSet();
private ObservableSet<PersonModel> persons = FXCollections.observableSet();
private ObservableSet<DeviceModel> devices = FXCollections.observableSet();
private ObservableMap<WallPosition, RoomModel> connectedRooms = FXCollections.observableHashMap();
private Room room; // the underlying room for the indoorpersontracker
public RoomModel() { this(-1); }
public RoomModel(int roomNumber) {
this.roomNumber.set(roomNumber);
this.room = MainApp.getInstance().getTracker().getRoom(roomNumber);
if(this.room == null) {
this.room = new Room(roomNumber);
// MainApp.getInstance().getTracker().addRoom(room);
}
}
/**
* connect this room with another room
* @param other
* @param isConnected true if the rooms are connected or false if they are disconnected
* @param position the position where the door is at
*/
public void setConnected(RoomModel other, boolean isConnected, WallPosition position) {
if (isConnected == true && !connectedRooms.containsValue(other)) { // prevent endless loop
if(this != BuildingModel.getSingleton().getOutsideInstance())
connectedRooms.put(position, other);
System.out.println("adding connection: " + position + ": " + other.getRoomNumber()); // TODO remove
other.setConnected(this, true, WallPosition.opposite(position)); // also tell the other room that it is connected with this room
if(this == BuildingModel.getSingleton().getOutsideInstance()) {
other.getRoom().setHasEntrance(true);
} else if(other == BuildingModel.getSingleton().getOutsideInstance()) {
room.setHasEntrance(true);
} else {
room.setConnected(other.getRoom(), true); // also connect the library rooms
}
} else if (isConnected == false && connectedRooms.containsValue(other)) {
if(this != BuildingModel.getSingleton().getOutsideInstance())
connectedRooms.remove(position);
other.setConnected(this, false, WallPosition.opposite(position));
if(this == BuildingModel.getSingleton().getOutsideInstance()) {
other.getRoom().setHasEntrance(false);
} else if(other == BuildingModel.getSingleton().getOutsideInstance()) {
room.setHasEntrance(false);
} else {
room.setConnected(other.getRoom(), false);
}
}
}
@XmlElement(name = "connections")
@XmlJavaTypeAdapter(ConnectionAdapter.class)
// @XmlElements({ @XmlElement(name = "WallPosition", type = WallPosition.class), @XmlElement(name = "RoomModel", type = RoomModelRef.class) })
public ObservableMap<WallPosition, RoomModel> getConnected() {
return this.connectedRooms;
}
// TODO remove
public void setConnected(ObservableMap<WallPosition, RoomModel> connections) {
this.connectedRooms = connections;
}
public void setHasEntrance(boolean hasEntrance, WallPosition position) {
this.room.setHasEntrance(hasEntrance);
// connect this room with the outside via this wall to draw a door
if(hasEntrance)
this.connectedRooms.put(position, BuildingModel.getSingleton().getOutsideInstance());
else
this.connectedRooms.remove(position);
}
public boolean getHasEntrance() {
return this.room.getHasEntrance();
}
public final IntegerProperty roomNumberProperty() {
return this.roomNumber;
}
@XmlAttribute(name = "roomNumber", required = true)
public final int getRoomNumber() {
return this.roomNumberProperty().get();
}
@XmlTransient
public Room getRoom() {
return room;
}
public void addSensor(Sensor sensor) {
sensors.add(sensor);
}
@XmlElement(name = "Sensor")
@XmlJavaTypeAdapter(SensorAdapter.class)
public ObservableSet<Sensor> getSensors() {
return sensors;
}
public void addPerson(PersonModel person) {
persons.add(person);
}
@XmlTransient
public ObservableSet<PersonModel> getPersons() {
return persons;
}
public void removePerson(PersonModel person) {
persons.remove(person);
}
public void addDevice(DeviceModel device) {
devices.add(device);
}
@XmlElement(name = "DeviceModel", type = DeviceModel.class)
public ObservableSet<DeviceModel> getDevices() {
return devices;
}
public void removeSensor(Sensor sensor) {
sensors.remove(sensor);
System.err.println("Sensor model removed from room " + getRoomNumber());
}
public void removeDevice(DeviceModel deviceModel) {
devices.remove(deviceModel);
}
public boolean hasTopWallDoor() {
return connectedRooms.containsKey(WallPosition.TOP);
}
public boolean hasBottomWallDoor() {
return connectedRooms.containsKey(WallPosition.BOTTOM);
}
public boolean hasLeftWallDoor() {
return connectedRooms.containsKey(WallPosition.LEFT);
}
public boolean hasRightWallDoor() {
return connectedRooms.containsKey(WallPosition.RIGHT);
}
public void setRoomNumber(int roomNumber) {
this.roomNumber.set(roomNumber);
this.room.setRoomNumber(roomNumber);
// migrate the persons in this room to the new room number
/*SimulatedSensor tempSensor = new SimulatedSensor(0);
// tempSensor.setRoom(room); TODO is currently in FloorModel.setRoomNoOffset, move to here if setting room number can be done elsewhere (could cause a problem with order then, add a Platform.runLater() here to resolve this)
MainApp.getInstance().getTracker().addSensor(tempSensor, roomNumber);
for(PersonModel person : persons) {
tempSensor.detectPerson(person.getName());
}
MainApp.getInstance().getTracker().removeSensor(tempSensor);*/
}
public boolean isConnectedWith(RoomModel roomModel) {
return connectedRooms.containsValue(roomModel);
}
@Override
public boolean equals(Object obj) {
if(obj == this) {
return true;
} else if(obj instanceof RoomModel) {
RoomModel roomModel = (RoomModel)obj;
return roomModel.room.equals(this.room);
} else {
return false;
}
}
@XmlID
public String getId() {
return String.valueOf(getRoomNumber());
}
void afterUnmarshal(Unmarshaller u, Object parent) {
System.err.println("connections: " + connectedRooms.size());
}
}
package at.jku.buildingsimulator.jaxb;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import at.jku.buildingsimulator.model.RoomModel;
import at.jku.buildingsimulator.model.RoomModel.WallPosition;
public class ConnectionAdapter extends XmlAdapter<ConnectionAdapter.AdaptedMap, Map<WallPosition, RoomModel>> {
public static class AdaptedMap {
//@XmlVariableNode("key")
@XmlElement(name = "connection", type = AdaptedEntry.class)
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
public static class AdaptedEntry {
@XmlElement(name = "WallPosition")
public WallPosition key;
@XmlElement(name="RoomModel")
@XmlJavaTypeAdapter(RoomModelAdapter.class)
public RoomModel value;
}
@Override
public AdaptedMap marshal(Map<WallPosition, RoomModel> map) throws Exception {
AdaptedMap adaptedMap = new AdaptedMap();
for(Entry<WallPosition, RoomModel> entry : map.entrySet()) {
AdaptedEntry adaptedEntry = new AdaptedEntry();
adaptedEntry.key = entry.getKey();
adaptedEntry.value = entry.getValue();
adaptedMap.entries.add(adaptedEntry);
}
return adaptedMap;
}
@Override
public Map<WallPosition, RoomModel> unmarshal(AdaptedMap adaptedMap) throws Exception {
List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
Map<WallPosition, RoomModel> map = new HashMap<>(adaptedEntries.size());
for(AdaptedEntry adaptedEntry : adaptedEntries) {
map.put(adaptedEntry.key, adaptedEntry.value);
}
System.err.println("unmarshal: " + map.size());
for(Entry<WallPosition, RoomModel> entry : map.entrySet()) {
System.err.println(entry.getKey() + ", " + entry.getValue());
}
return map;
}
}
package at.jku.buildingsimulator.jaxb;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import at.jku.buildingsimulator.model.RoomModel;
public class RoomModelAdapter extends XmlAdapter<RoomModelRef, RoomModel>{
@Override
public RoomModelRef marshal(RoomModel v) throws Exception {
return new RoomModelRef(v);
}
@Override
public RoomModel unmarshal(RoomModelRef v) throws Exception {
return v.roomModel;
}
}
package at.jku.buildingsimulator.jaxb;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlIDREF;
import at.jku.buildingsimulator.model.RoomModel;
public class RoomModelRef {
@XmlIDREF
@XmlAttribute(name = "ref")
RoomModel roomModel;
public RoomModelRef() { }
public RoomModelRef(RoomModel roomModel) {
this.roomModel = roomModel;
}
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<BuildingModel>
<FloorModel floorNo="1" numRooms="4" roomNoOffset="0">
<roomModels>
<entry>
<key>1</key>
<value roomNumber="1">
<connections>
<connection>
<WallPosition>RIGHT</WallPosition>
<RoomModel ref="2"/>
</connection>
</connections>
<id>1</id>
</value>
</entry>
<entry>
<key>2</key>
<value roomNumber="2">
<connections>
<connection>
<WallPosition>LEFT</WallPosition>
<RoomModel ref="1"/>
</connection>
<connection>
<WallPosition>BOTTOM</WallPosition>
<RoomModel ref="4"/>
</connection>
</connections>
<id>2</id>
</value>
</entry>
<entry>
<key>3</key>
<value roomNumber="3">
<connections/>
<id>3</id>
</value>
</entry>
<entry>
<key>4</key>
<value roomNumber="4">
<connections>
<connection>
<WallPosition>RIGHT</WallPosition>
<RoomModel ref="0"/>
</connection>
<connection>
<WallPosition>TOP</WallPosition>
<RoomModel ref="2"/>
</connection>
</connections>
<id>4</id>
</value>
</entry>
</roomModels>
</FloorModel>
<outsideInstance roomNumber="0">
<connections/>
<id>0</id>
</outsideInstance>
<persons/>
</BuildingModel>
我还在其中添加了一些调试行:在ConnectionAdapter中,在unmarshal()方法中,它表示:“unmarshal:1”for the first room。在RoomModel中,在afterUnmarshal()处,它表示:“connections:0”for the first room,我不太明白这一点,因为很明显,解组在ConnectionAdapter中工作,所以错误一定在这两者之间。
我希望有人花时间给它一个“快速”看看,不幸的是,我不能张贴整个模型代码,因为它是非常复杂的,与这个问题无关。
编辑:好的,我现在在ConnectionAdapter中的unmarshall()方法中打印了解封映射:
@Override
public Map<WallPosition, RoomModel> unmarshal(AdaptedMap adaptedMap) throws Exception {
List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
Map<WallPosition, RoomModel> map = new HashMap<>(adaptedEntries.size());
for(AdaptedEntry adaptedEntry : adaptedEntries) {
map.put(adaptedEntry.key, adaptedEntry.value);
}
System.err.println("unmarshal: " + map.size());
for(Entry<WallPosition, RoomModel> entry : map.entrySet()) {
System.err.println(entry.getKey() + ", " + entry.getValue());
}
return map;
}
unmarshal: 1
RIGHT, null
connections: 0
unmarshal: 2
LEFT, at.jku.buildingsimulator.model.RoomModel@21f654e2
BOTTOM, null
connections: 0
unmarshal: 0
connections: 0
unmarshal: 2
RIGHT, null
TOP, at.jku.buildingsimulator.model.RoomModel@2b13651e
connections: 0
Rooms in here: 4
unmarshal: 0
connections: 0
好吧,我现在自己想出了一个解决办法。
问题在于解组的顺序,这导致当读取连接中的引用时,并不是所有的RoomModel都已经被读取。我以为JAXB会很聪明地保存这个引用并在以后解决它,但不,事实并非如此。
我通过简单地修改模型解决了这个问题,特别是将映射connectedRooms的类型更改为
,以删除循环引用并简化序列化。我真的不知道为什么我之前会想到这个问题,当我想到我为这个问题所花费的工作和时间时,它让我兴奋不已,但至少我在循环引用方面有了一些经验,而且(联合国)编组的顺序很重要。
我正在尝试使用将XML转换为对象,我的XML如下所示: 所以我创建了3个java类:EntityResource.java,Item.java和PromissionRecipient.java如下图所示: EntityResource.java Item.java PermissionRecipient.java 所有工作和我得到了一个EntityResources对象包含项目,但EntityRe
问题内容: 我正在尝试与JAXB进行封送处理。 我的输出就像 但是我需要像这样的输出 我正在使用以下代码来执行此操作。如果我取消注释代码,则会出现属性绑定异常。没有它,我可以编译,但无法获得所需的确切输出。 和我的豆lo 转接器类别 问题答案: 您可以执行以下操作: 适配器CDATA 根 该注释用于指定的应该被使用。 演示版 我必须包装一个以获得所需的效果。另请注意,设置方法意味着它负责该操作的所
问题内容: 我正在尝试创建一个非常简单的REST服务器。我只是有一个测试方法,它将返回字符串列表。这是代码: 它给出以下错误: 我希望JAXB对诸如String,Integer等简单类型具有默认设置。我想不是。这是我的想象: 使这种方法最简单的方法是什么? 问题答案: 我使用@LiorH的示例并将其扩展为: 注意,它使用泛型,因此您可以将其与String之外的其他类一起使用。现在,应用程序代码很简
问题内容: 我在使用Marshaller.JAXB_FRAGMENT属性成功编组时遇到了一些麻烦。这是我尝试输出的XML的简单版本。 的和元件基本上围住了大量的只是容器元素&元件。我目前正在尝试在马歇尔(Marshall)上编组。 是否有可能先将and 元素编组,然后再将其编组到element 上,然后将输出包含在标记中? 当我在WorkSet级别进行封送处理时,它将属性附加到WorkSet标记中
问题内容: 我们正在整理来自http://xmlgw.companieshouse.gov.uk/的回复。这是发送给马歇尔的文本: CoSearchItem的模型如下: NameSearch模型具有以下结构: 该软件包具有以下注释: 解组从第一完成提取从一个更大的,内部的产品清单。但是,当我们解析xml时,CoSearchItem中的所有字段都设置为null,无法找出原因。 问题答案: 您需要使用