xmlutil icon indicating copy to clipboard operation
xmlutil copied to clipboard

Multiple element attributes

Open rmpt opened this issue 3 years ago • 5 comments

My goal is to build something similar to this:

<MyXml
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="urn:OECD:MyXmlFile" 
    xsi:schemaLocation="urn:OECD:MyXmlFile.xsd">
</MyXml>

But with XmlSerialName annotation I can only have 1 namespace. How can I add more than one? I don't see an option to add custom attributes to an element, that would solve the issue too.

rmpt avatar Apr 12 '21 11:04 rmpt

The way the system works is that namespaces are handled automatically (although not necessarily optimally - it doesn't look at what other namespaces may be used by child tags - but will use prefixes found in the document). What you can do is have a custom serializer for the outer tag and in that serializer (if you find that the encoder implements Xml.XmlOutput you can then use the target property to get the xml writer. Write the container using the regular encoder beginStructure... functions, write the additional attributes, and serialize the children using the encoder. Of course you can set the namespace on the schemaLocation attribute and then it should work automatically.

pdvrieze avatar Apr 17 '21 15:04 pdvrieze

Can you make an example of both scenarios? I understand the idea, but having some issues putting it in place.

rmpt avatar May 15 '21 21:05 rmpt

I've just pushed an example to dev to show how it works (note that it uses the @XmlBefore annotation (that is not released yet) - but merely as an example for how to fine-tune things. It works fine without, but the order could be different for the attributes. https://github.com/pdvrieze/xmlutil/blob/dev/examples/src/main/kotlin/net/devrieze/serialization/examples/customserializer/MyXmlManual.kt

pdvrieze avatar May 16 '21 21:05 pdvrieze

To be honest it's quite dirty solution imo. It would be great if we could somehow provide multiple namespaces through an annotation, without a need to implement whole serializer 🤔

At this moment I'm playing around something like this, as I can't achieve it though annotations:

@Serializable
@SerialName("metadata")
internal data class Metadata(
    @XmlElement(false)
    val xmlns: String = "http://maven.apache.org/METADATA/1.1.0",
    @XmlElement(false)
    @SerialName("xmlns:xsi")
    val xmlnsXsi: String = "http://www.w3.org/2001/XMLSchema-instance",
    @XmlElement(false)
    @SerialName("xsi:schemaLocation")
    val xsiSchemaLocation: String = "http://maven.apache.org/METADATA/1.1.0 http://maven.apache.org/xsd/metadata-1.1.0.xsd",
)

The output is correct:

<?xml version='1.0' encoding='UTF-8'?>
<metadata xmlns="http://maven.apache.org/METADATA/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/METADATA/1.1.0 http://maven.apache.org/xsd/metadata-1.1.0.xsd">
</metadata>

But unfortunately I'm not able to load this:

Could not find a field for name {http://www.w3.org/2001/XMLSchema-instance}schemaLocation
  candidates: xmlns, xmlns:xsi, xsi:schemaLocation, groupId, artifactId, version, versioning at position [row,col {unknown-source}]: [2,1]
nl.adaptivity.xmlutil.serialization.UnknownXmlFieldException: Could not find a field for name {http://www.w3.org/2001/XMLSchema-instance}schemaLocation
  candidates: xmlns, xmlns:xsi, xsi:schemaLocation, groupId, artifactId, version, versioning at position [row,col {unknown-source}]: [2,1]
	at nl.adaptivity.xmlutil.serialization.XmlConfig$Companion$DEFAULT_UNKNOWN_CHILD_HANDLER$1.invoke(XmlConfig.kt:191)
	at nl.adaptivity.xmlutil.serialization.XmlConfig$Companion$DEFAULT_UNKNOWN_CHILD_HANDLER$1.invoke(XmlConfig.kt:187)
	at nl.adaptivity.xmlutil.serialization.DefaultXmlSerializationPolicy.handleUnknownContent(XmlSerializationPolicy.kt:217)
	at nl.adaptivity.xmlutil.serialization.XmlConfig$unknownChildHandler$1.invoke(XmlConfig.kt:100)
	at nl.adaptivity.xmlutil.serialization.XmlConfig$unknownChildHandler$1.invoke(XmlConfig.kt:47)
	at nl.adaptivity.xmlutil.serialization.XmlDecoderBase$TagDecoder.indexOf(XMLDecoder.kt:474)
	at nl.adaptivity.xmlutil.serialization.XmlDecoderBase$TagDecoder.decodeElementIndex(XMLDecoder.kt:513)

I'm also not able to use it as a final fields outside of the constructor. Do you think I can somehow do something like this, even a little dirty, without implementing custom serializer?

dzikoysk avatar Aug 16 '21 14:08 dzikoysk

The example would be better when using the following:

@Serializable
@XmlSerialName("metadata", "http://maven.apache.org/METADATA/1.1.0", "")
internal data class Metadata(
    @XmlElement(false)
    @XmlSerialName("schemaLocation", "http://www.w3.org/2001/XMLSchema-instance", "xsi")
    val xsiSchemaLocation: String = "http://maven.apache.org/METADATA/1.1.0 http://maven.apache.org/xsd/metadata-1.1.0.xsd",
)

You shouldn't specify namespaces (or prefixes) using @SerialName as that will not work correctly. The way namespaces work is that the parsing doesn't/shouldn't care about the prefix used. This format is designed with namespaces, so ignoring namespaces is not likely to work. Specifying a namespace as a property cannot be correct as namespaces should not be variable.

pdvrieze avatar Aug 23 '21 10:08 pdvrieze