Kotlin and SimpleXML - Unable to satisfy ElementList Error - kotlin

I'm struggling to get simpleXML and Kotlin to read a XML file properly.
I've got the following Root class:
class ServerConfiguration {
#field:Root(strict = false, name = "serverConfiguration")
#field:ElementList(required = true, name = "channels", entry = "channel", inline = true, type=Channel::class)
lateinit var channels: List<Channel>
#field:Element(name = "serverSettings", required = true, type = ServerSettings::class)
lateinit var serverSettings: ServerSettings
}
(The Channel class itself has also Lists, but even if I leave it with simple Attributes (ie Strings), it won't work.)
The XML contains:
<serverConfiguration version="3.5.2">
<date>2022-07-12 10:57:47</date>
<channelGroups>
[... lots of groups]
</channelGroups>
<channels>
<channel version="3.5.2">
<id>b7cb6bf9-d3a5-4a74-8399-b6689d915a15</id>
<nextMetaDataId>6</nextMetaDataId>
<name>cANACR_1_Fhir2Hl7Omg</name>
<connector class="[...].TcpReceiverProperties" version="3.5.2">
[... more ]
</channel>
[... a lot of channels]
</channels>
[... even more data]
</serverConfiguration>
Since there are multiple Tags in the xml that contain a "class" Attribute, I understand that I need to use inline = true in my #field:ElementList
I got through a lot of errors up until this point which I could resolve by myself but this one eludes me.
I run the Serializer via:
val serializer: Serializer = Persister()
val dataFetch = serializer.read(ServerConfiguration::class.java, myFile!!, false)
The Error (I cut out classpaths):
org.simpleframework.xml.core.ValueRequiredException: Unable to satisfy #org.simpleframework.xml.ElementList(entry="channel", data=false, inline=true, name="channels", type=Channel.class, required=true, empty=true) on field 'channels' private java.util.List ServerConfiguration.channels for class ServerConfiguration at line 1
If anyone could nudge me in the right direction, I'd be very grateful.
Addendum:
If I set required=false the program runs, but not a single channel is read.
I've tried ArrayList, List, and Set as datatype
I've tried to circumvent lateinit with var channels: List<Channel> = mutableListOf()

I've got it working through adding a wrapper class for the nested lists:
ServerConfiguration.kt:
[...]
#field:Element(name="channels", required = true, type=ChannelList::class)
lateinit var channelList: ChannelList
ChannelList.kt:
class ChannelList {
#field:ElementList(required = true, inline = true,name = "channels", entry = "channel", type=Channel::class, data = true, empty=false)
var channels: List<Channel> = mutableListOf()
}
And finally Channel.kt:
class Channel {
#field:Element(name = "destinationConnectors", required = true, type = DestinationConnectorList::class)
lateinit var destinationConnectorList: DestinationConnectorList
#field:Element(name = "exportData", required = true, type=ExportData::class)
lateinit var exportData: ExportData
[...]
While this is working, I would have expected simpleXML to be able to add the Elements of the List directly without needing to use a wrapper class (ChannelList).
If anyone knows how to achieve this, please feel free to comment or add your solution.
Kind regards,
K

Related

MapStruct Property has no write accessor in MetaData for target name

I have a FlatDTO that needs to be mapped to a nested Response containing InfoData and MetaData
The code for the response is generated by OpenAPI. So the below definitions can't be changed.
package org.mapstruct.example.kotlin.openapi
import com.fasterxml.jackson.annotation.JsonProperty
import javax.validation.Valid
data class Response(
#field:Valid #field:JsonProperty("infoData", required = true) val infoData: InfoData,
#field:Valid #field:JsonProperty("metaData", required = true) val metaData: MetaData
)
data class InfoData(
#field:JsonProperty("id", required = true) val id: kotlin.String,
)
data class MetaData(
#field:JsonProperty("firstProperty") val firstProperty: String? = null,
)
I have defined FlatDTO as follows.
package org.mapstruct.example.kotlin.models
data class FlatDTO(
var id: String? = null,
var firstProperty: String,
)
Here is my mapper class which maps FlatDTO to Response
package org.mapstruct.example.kotlin.mapper
import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.Mappings
import org.mapstruct.example.kotlin.models.FlatDTO
import org.mapstruct.example.kotlin.openapi.Response
#Mapper
interface DataMapper {
#Mappings(
Mapping(target = "infoData.id", source = "id"),
Mapping(target = "metaData.firstProperty", source = "firstProperty")
)
fun flatToResponse(flatDTO: FlatDTO): Response
}
When I try to build the code using mvn clean install
I get the following error.
error: Property "firstProperty" has no write accessor in MetaData for target name "metaData.firstProperty".
[ERROR] #org.mapstruct.Mappings(value = {#org.mapstruct.Mapping(target = "infoData.id", source = "id"), #org.mapstruct.Mapping(target = "metaData.firstProperty", source = "firstProperty")})
I understand that this message is trying to say that there is no setter function for firstProperty because it's defined as val but that code cannot be edited. I can write my own custom mapper that works just fine.
I wanted to understand if there is a way to use MapStruct in this scenario.

Klint and spotless: com.pinterest.ktlint.core.ParseException: Expecting a parameter declaration

I'm getting weird exception when trying to run ./gradlew spotlessApply on my project in Kotlin.
Class causing the problem:
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class CurrentFluttering(
#PrimaryKey var id: Long = 0,
var currentCoinsHeap: Int = 0,
var currentEarnedCoins: Int = 0,
var startTime: Long = 0,
var pauseTime: Long = 0,
var time: Long = 0,
var firstCycle: Boolean = true,
var inBackground: Boolean = false,
var currentMissedCoins: Int = 0,
var isPaused: Boolean = false,
) : RealmObject()
Stack trace:
> Task :spotlessKotlin FAILED
Step 'ktlint' found problem in 'app/src/main/java/com/cfhero/android/model/state/CurrentFluttering.kt':
Expecting a parameter declaration
com.pinterest.ktlint.core.ParseException: Expecting a parameter declaration
at com.pinterest.ktlint.core.KtLint.format(KtLint.kt:357)
at sun.reflect.GeneratedMethodAccessor35.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.diffplug.spotless.kotlin.KtLintStep$State.lambda$createFormat$1(KtLintStep.java:173)
at com.diffplug.spotless.FormatterFunc.apply(FormatterFunc.java:31)
at com.diffplug.spotless.FormatterStepImpl$Standard.format(FormatterStepImpl.java:78)
at com.diffplug.spotless.FormatterStep$Strict.format(FormatterStep.java:76)
at com.diffplug.spotless.Formatter.compute(Formatter.java:230)
at com.diffplug.spotless.Formatter.applyToAndReturnResultIfDirty(Formatter.java:192)
at com.di
Have anybody experienced same or similar problem?
I found it. The problem was dangling ',' after last parameter in constructor (which Kotlin allows ).
var isPaused: Boolean = false, <- here ლ(ಠ益ಠლ)
) : RealmObject()
This is caused due to the default ktlint version used by the plugin. As of this time it's private static final String DEFAULT_VERSION = "0.35.0" which does not support trailing commas, although kotlin 1.4 does. In fact they had updated it to 0.40.0 at some point but got some formatting issues whiplash and eventually reverted the changes.
If you want though you can manually point to the latest version with something like
spotless {
kotlin {
target '**/*.kt'
ktlint("0.40.0")
}
}

How to reference OpenAPI Generator task properties

I am attempting to reference assigned property generatorName when setting outputDir.
Attempted to reference generatorName property using same syntax as other task properties (i.e. $buildDir). Also attempted to more fully qualify the property name openApiGenerator.generatorName.
openApiGenerate {
verbose = false
generatorName = "html2" // assignment to property
inputSpec = "$buildDir/swagger/testing.yml".toString()
//outputDir = "$buildDir/generated".toString()
outputDir = "$buildDir/generated/$generatorName".toString() // fails
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
// debugging code
println " buildDir: $buildDir".toString()
println " generatorName: $generatorName".toString() // this fails
}
output from debugging code shows failure to reference generatorName property:
> Configure project :
buildDir: C:\Users\jgunchy\repos\testingproject\build
generatorName: property(class java.lang.String, fixed(class java.lang.String, html2))
This is an observable property, not a string. You should be able to access the underlying string using .get() like this:
openApiGenerate {
verbose = false
generatorName = "html2"
inputSpec = "$buildDir/swagger/testing.yml".toString()
outputDir = "$buildDir/generated/${generatorName.get()}".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
}
Another option would be to use configuration rather than the project extension container's properties directly. For instance, add to gradle.properties:
generatorName=html2
Then, your configuration would look like this:
openApiGenerate {
verbose = false
generatorName = project.ext.generatorName
inputSpec = "$buildDir/swagger/testing.yml".toString()
outputDir = "$buildDir/${project.ext.generatorName}".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
}
$buildDir is a getter on the Project instance with a toString() method that happens to output the File path, which is why it behaves differently.

How to save List<Point> into preferences and get List<Point> from preferences in flutter?

Error while using json_serializable
json_serializable:json_serializable on .../sign_point_model.dart:
Error running JsonSerializableGenerator
Could not generate fromJson code for valList because of type Point<num>.
None of the provided TypeHelper instances support the defined type.
json_serializable doesn't know how to convert a Point into JSON. Since you know it's just a pair of nums you could easily convert the list yourself.
import 'dart:convert';
void main() async {
var points = [
Point(Offset(123, 456), PointType.tap),
Point(Offset(3.14159, 3.16227), PointType.move),
];
var simplified =
points.map((e) => [e.offset.dx, e.offset.dy, e.type.index]).toList();
String j = json.encode(simplified);
print(j);
var decoded = json.decode(j) as List;
var asPoints = decoded
.map((e) => Point(Offset(e[0], e[1]), PointType.values[e[2]]))
.toList();
print(asPoints);
}

How do i create a TCP receiver that only consumes messages using akka streams?

We are on: akka-stream-experimental_2.11 1.0.
Inspired by the example
We wrote a TCP receiver as follows:
def bind(address: String, port: Int, target: ActorRef)
(implicit system: ActorSystem, actorMaterializer: ActorMaterializer): Future[ServerBinding] = {
val sink = Sink.foreach[Tcp.IncomingConnection] { conn =>
val serverFlow = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(message => {
target ? new Message(message); ByteString.empty
})
conn handleWith serverFlow
}
val connections = Tcp().bind(address, port)
connections.to(sink).run()
}
However, our intention was to have the receiver not respond at all and only sink the message. (The TCP message publisher does not care about response ).
Is it even possible? to not respond at all since akka.stream.scaladsl.Tcp.IncomingConnection takes a flow of type: Flow[ByteString, ByteString, Unit]
If yes, some guidance will be much appreciated. Thanks in advance.
One attempt as follows passes my unit tests but not sure if its the best idea:
def bind(address: String, port: Int, target: ActorRef)
(implicit system: ActorSystem, actorMaterializer: ActorMaterializer): Future[ServerBinding] = {
val sink = Sink.foreach[Tcp.IncomingConnection] { conn =>
val targetSubscriber = ActorSubscriber[Message](system.actorOf(Props(new TargetSubscriber(target))))
val targetSink = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(Message(_))
.to(Sink(targetSubscriber))
conn.flow.to(targetSink).runWith(Source(Promise().future))
}
val connections = Tcp().bind(address, port)
connections.to(sink).run()
}
You are on the right track. To keep the possibility to close the connection at some point you may want to keep the promise and complete it later on. Once completed with an element this element published by the source. However, as you don't want any element to be published on the connection, you can use drop(1) to make sure the source will never emit any element.
Here's an updated version of your example (untested):
val promise = Promise[ByteString]()
// this source will complete when the promise is fulfilled
// or it will complete with an error if the promise is completed with an error
val completionSource = Source(promise.future).drop(1)
completionSource // only used to complete later
.via(conn.flow) // I reordered the flow for better readability (arguably)
.runWith(targetSink)
// to close the connection later complete the promise:
def closeConnection() = promise.success(ByteString.empty) // dummy element, will be dropped
// alternatively to fail the connection later, complete with an error
def failConnection() = promise.failure(new RuntimeException)