DeepCopy complex object with cyclic references and custom getters using Serialization

I wrote a complex Kotlin class Env in which I need to implement a deepCopy method. Here is a minimal reproductive example of the class and its referenced classes:

@Serializable
class Env(
    override var time: Int,
    override var workstationPool: List<Workstation>,
    override var jobPool: MutableList<Order>,
    override val jobArrivalEvents: List<JobArrivalEvent>,
    override val breakdownEvents: List<BreakdownEvent>,
) : AbstractEnvironment() {

    override fun step(actionMap: Pair<MutableMap<Long, OrderAction>, MutableMap<Long, OperationDetails?>>)
            : Triple<Boolean, List<Workstation>, MutableList<Order>> {
        handleJobEvents()
        handleDisruptiveEvents()

        for (workstation in workstationPool) {
            if (actionMap.second[workstation.id] != null) {
                workstation.allocateOperation(actionMap.second[workstation.id]!!, time)
            }
            workstation.process(time)
        }

        val timeBufferList = mutableListOf<Int>()
        for (job in jobPool) {
            if (actionMap.first[job.id] == OrderAction.RELEASE && job.getState() == OrderState.RESTRICTED) {
                logger.info { "Releasing order ${job.name}" }
                job.release(time)
            }
            job.updateOrder(time)

            timeBufferList.addAll(job.getTimeBufferList())
        }

        jobPool.forEach { job ->
            job.updateShopCapacity(
                workstationPool.count { it.state != WorkstationState.FAILURE },
                jobPool.count { it.getState() == OrderState.PROCESSING }
            )

            job.details.operations.filter { it.assignee == null }.forEach { operationDetails ->
                val relTimeBuffer = if (timeBufferList.max() != 0) operationDetails.timeBuffer.div(timeBufferList.max().toFloat()) else 1F
                operationDetails.relTimeBuffer = relTimeBuffer
            }
        }

        time += 1

        val terminated = jobPool.all { it.getState() == OrderState.DONE }
        return Triple(terminated, workstationPool, jobPool)
    }

    fun deepCopy(): DiscreteSchedulingEnv {
        return deepCopy(this)
    }

}
@Serializable
data class WorkstationDetails(
    val id: Long,
    val name: String,
    @EncodeDefault var state: WorkstationState = WorkstationState.IDLE,
    @EncodeDefault var workload: Int = 0,
    @EncodeDefault var numSetupChanges: Int = 0,
    @EncodeDefault var operationLog: MutableList<OperationDetails> = mutableListOf(),
    @EncodeDefault var downtime: Int = 0,
    @EncodeDefault val breakdownEvents: MutableList<BreakdownEvent> = mutableListOf()
) {}

@Serializable
class Workstation(val assetDetails: WorkstationDetails):
    AbstractAsset<NoopClient>() {

    override val id: Long = assetDetails.id
    override val name: String = assetDetails.name

    override val clientConfiguration: ClientConfiguration<NoopClient>
        get() = TODO("Not yet implemented")

    var state: WorkstationState
        get() = assetDetails.state
        private set(value) {
            assetDetails.state = value
        }

    private var allocatedOperation: OperationDetails? = null

    fun getAllocatedOperation(): OperationDetails?{
        return allocatedOperation
    }

fun allocateOperation(operation: OperationDetails, time: Int) {
        if (state != WorkstationState.IDLE ) return

        if (allocatedOperation == null) {
            allocatedOperation = operation
            allocatedOperation!!.startTime = time
            allocatedOperation!!.assignee = id
            val setupTime = operation.getSetupTimeForProductType(getProductTypeOfLastOperation())
            allocatedOperation!!.setupTime = setupTime
            allocatedOperation!!.remainingSetupTime = setupTime
            if (getProductTypeOfLastOperation() != null){
                if (getProductTypeOfLastOperation() != operation.orderDetailsType) assetDetails.numSetupChanges += 1
            } else assetDetails.numSetupChanges += 1
            allocatedOperation!!.plannedEndTime = time + allocatedOperation!!.setupTime + allocatedOperation!!.duration
        }
    }

    fun process(time: Int) {
        when (state) {
            WorkstationState.IDLE -> processTaskAllocation()
            WorkstationState.PROCESSING -> handleTaskExecution(allocatedOperation!!, time)
            WorkstationState.FAILURE -> {
                assetDetails.downtime += 1
                if (allocatedOperation != null) {
                    logger.info { "Workstation $id can not process operation (ID / name) ${allocatedOperation!!.name} / ${allocatedOperation!!.name} due to an existing breakdown." }
                }
            }
        }
    }

}
@Serializable
data class OrderDetails(
    val id: Long,
    val name: String,
    val type: ProductType,
    @EncodeDefault val description: String = "",
    @EncodeDefault var timeBuffer: Int = 0,
    val operations: List<OperationDetails>,
    @EncodeDefault var incomeTime: Int = 0,
    @EncodeDefault val plannedDeliveryTime: Int = 0,
    @EncodeDefault var actualDeliveryTime: Int = 0,
    @EncodeDefault var releaseTime: Int = 0,
    @EncodeDefault var shopCapacity: Int = 0,
    @EncodeDefault var state: OrderState = OrderState.RESTRICTED
) {}

@Serializable
class Order(override val id: Long,
            val productType: ProductType,
            val deliveryTime: Int,
            val incomeTime: Int = 0
    ) : AbstractAsset<NoopClient>() {
    override var name: String = "$productType-order-$id"
    private var operations = getOperations(productType) // fixme - getters not included in serialization
    private var orderDetails: OrderDetails = OrderDetails(
        id = id,
        type = productType,
        name = name,
        operations = operations,
        plannedDeliveryTime = deliveryTime,
        incomeTime = incomeTime
    )

    override val clientConfiguration: ClientConfiguration<NoopClient>
        get() = TODO("Not yet implemented")

    fun getState(): OrderState {
        return orderDetails.state
    }

    val details: OrderDetails get() = orderDetails

    fun release(time: Int) {
        if (orderDetails.state != OrderState.RESTRICTED) return

        orderDetails.state = OrderState.PROCESSING
        orderDetails.releaseTime = time
    }

        fun updateOrder(time: Int) {
        when (orderDetails.state) {
            OrderState.RESTRICTED -> {
                updateDeliveryTimeBuffer(time)
                logger.info { "Order $name is not released yet." }
            }
            OrderState.PROCESSING -> {
                orderDetails.operations.filter { it.state == OperationState.RESTRICTED }.forEach { updateOperationState(it) }
                if (orderDetails.operations.all { it.state == OperationState.DONE }) {
                    orderDetails.state = OrderState.DONE
                    orderDetails.actualDeliveryTime = time
                    orderDetails.timeBuffer = orderDetails.plannedDeliveryTime.minus(time)
                    logger.info { "Order $name is finished." }
                }
                else {
                    updateDeliveryTimeBuffer(time)
                    logger.info { "Order $name is in process." }
                }
            }
            OrderState.DONE -> logger.info { "Order $name is finished." }
        }
    }
}
@Serializable
data class OperationDetails(
    val id: Long,
    val duration: Int,
    val name: String,
    val previous: List<OperationDetails>,
    var cumulativeProcessTime: Int = 0,
    var orderDetailsType: ProductType,
    var orderDetailsName: String = "",
    @EncodeDefault var assignee: Long? = null,
    @EncodeDefault var startTime: Int? = null,
    @EncodeDefault var timeBuffer: Int = 0,
    @EncodeDefault var setupTime: Int = 0,
    @EncodeDefault var remainingProcessTime: Int = duration,
    @EncodeDefault var remainingSetupTime: Int = setupTime,
    @EncodeDefault var plannedEndTime: Int = 0,
    @EncodeDefault var actualEndTime: Int = 0,
    @EncodeDefault var state: OperationState = OperationState.RESTRICTED,
    @EncodeDefault var relTimeBuffer: Float = 0F,
    @EncodeDefault val setupTimeMatrix: MutableMap<ProductType, List<Int>> = OrderSetupMatrix.setupTimeMatrix
) {}

@Serializable
class Operation(override val id: Long, override val name: String, val assetDetails: OperationDetails
) : AbstractAsset<NoopClient>(){
    override val clientConfiguration: ClientConfiguration<NoopClient>
        get() = TODO("Not yet implemented")

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Operation

        if (id != other.id) return false
        if (name != other.name) return false
        if (assetDetails != other.assetDetails) return false

        return true
    }

    override fun hashCode(): Int {
        var result = id.hashCode()
        result = 31 * result + name.hashCode()
        result = 31 * result + assetDetails.hashCode()
        return result
    }
}
@Serializable
data class BreakdownEvent(
    val workstationId: Long,
    val startTime: Int,
    var endTime: Int?
)

@Serializable
data class JobArrivalEvent(
    val arrivalTime: Int,
    val deliveryTime: Int,
    val productType: ProductType,
)

I already tried to implement a deepCopy method by using kotlinx serialization to encode into a json and decode again afterwards:

object SerializationUtil {
    inline fun <reified T> deepCopy(obj: T): T {
        val json = Json
        val jsonString = json.encodeToString(serializer(), obj)
        return json.decodeFromString(serializer(), jsonString)
    }
}

I wrote a test method to compare two Env objects. If I initialize an instance of Env and use the deepCopy method, all properties are similar. However, when using the Env.step() method (which modifies metrics like the state and timeBuffer of operations) on both instances of Env, the properties of some Operation instances differ. How can I properly deepCopy an Env object? What am I missing?

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật