当前位置: 首页 > 知识库问答 >
问题:

JAXB映射未解编组,尽管编组工作正常

卢永寿
2023-03-14

..循环引用解决方案来自这里: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

共有1个答案

许沛
2023-03-14

好吧,我现在自己想出了一个解决办法。

问题在于解组的顺序,这导致当读取连接中的引用时,并不是所有的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,无法找出原因。 问题答案: 您需要使用