Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to remove type information with polymorphic output? #175

Closed
johanvergeer opened this issue Aug 22, 2021 · 6 comments
Closed

How to remove type information with polymorphic output? #175

johanvergeer opened this issue Aug 22, 2021 · 6 comments
Labels
question Further information is requested state:waiting for response Waiting for response from submitter

Comments

@johanvergeer
Copy link

I'm working on a dsl for Azure DevOps YAML Pipelines and they have a block called variables that can contain a few types.

variables:
  # a variable can be created in 2 different ways
  - myVar:  'myVarValue'
  - name: myVar
     value: 'myVarValue'
     readonly: true
  # a group can be created in 1 way, but always has the same key
  - group: myFirstGroup
  - group: mySecondGroup
  - group: myThirdGroup

I've been working on this for a while now and by now I have been able to create a list of Variable

@Serializable
abstract class VariableBase {
    abstract val name: String
}

@Serializable
class Variable(override val name: String, val value: String, val readonly: Boolean = false) : VariableBase()

@Serializable
data class Group(override val name: String) : VariableBase()

@Serializable
class Variables {
    var variables = mutableListOf<VariableBase>()

    fun variable(name: String, value: String, readonly: Boolean = false) {
        this.variables.add(Variable(name, value, readonly))
    }

    fun group(name: String) {
        this.variables.add(Group(name))
    }
}

val variables = Variables()
variables.variable("myVar", "myValue")
variables.group("myFirstGroup")
variables.group("mySecondGroup")
variables.group("myThirdGroup")

val result = Yaml(
    configuration = YamlConfiguration(
        polymorphismStyle = PolymorphismStyle.Property
    ),
    serializersModule = SerializersModule {
        polymorphic(VariableBase::class) {
            subclass(Variable::class)
            subclass(Group::class)
        }
    })
    .encodeToString(Variables.serializer(), variables)

print(result)

This will generate the following output:

variables:
- type: "Variable"
  name: "myVar"
  value: "myValue"
  readonly: false
- type: "Group"
  name: "myFirstGroup"
- type: "Group"
  name: "mySecondGroup"
- type: "Group"
  name: "myThirdGroup"

So how would I be able to create the list that is required to create an Azure DevOps YAML Pipeline variables block?

I guess it might be an idea to add a PolymorphismStyle.None to prevent a type output. I'll create a PR if you think that is a good idea.

@johanvergeer
Copy link
Author

I was just able to rename the name field of the Group class to group using @SerialName("group")

@Serializable
data class Group(@SerialName("group") override val name: String) : VariableBase()

This gives the following output and brings me one step closer to the solution.

variables:
- type: "Variable"
  name: "myVar"
  value: "myValue"
  readonly: false
- type: "Group"
  group: "myFirstGroup"
- type: "Group"
  group: "mySecondGroup"
- type: "Group"
  group: "myThirdGroup"

Now I just need to get rid of the type field. Do you think my previous suggestion will work?

@johanvergeer johanvergeer changed the title How to create a list of pairs How to remove type information with polymorphic output? Aug 22, 2021
@johanvergeer
Copy link
Author

The only issue I see at this moment is that this can be used to serialise. But it will be hard to deserialise when the type is not known.

Is it ok when that would be a known limitation of PolymorphismStyle.None?

@charleskorn
Copy link
Owner

Hi @johanvergeer, in this case, I'd suggest creating a custom serializer for the VariableBase.

On serialization, it could check the type of the input object and call directly into the derived class' serializer to output the value without any decoration (eg. a type field).

If you also need deserialization, it could take the Decoder instance passed to deserialize() and cast it to YamlInput and examine the YAML node directly, determine what type of variable it represents and then delegate to the appropriate derived class serializer. (This is an example of this technique - it's choosing a different path depending on the type of YAML input.)

(I'm hesitant to add support for PolymorphismStyle.None as this would only work for serialization, not deserialization.)

@charleskorn
Copy link
Owner

Hi @johanvergeer, did that work for your situation?

@charleskorn charleskorn added question Further information is requested state:waiting for response Waiting for response from submitter labels Sep 4, 2021
@johanvergeer
Copy link
Author

Hi @charleskorn I'm pretty busy at the moment at work, so I haven't been able to get it working yet. (Also because I'm no expert so I need some figuring out 😉)

I will give it a try later this week.

@johanvergeer
Copy link
Author

Hi @charleskorn That works like a charm. Thanks for your help. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested state:waiting for response Waiting for response from submitter
Projects
None yet
Development

No branches or pull requests

2 participants