Azure / Azure/typespec-azure

[typespec-go] Fix XML alias generation for repeated models and unwrapped arrays

Open
#5,470 0 comments 0 reactions 1 assignee Claimed by @jhendrixMSFT View on GitHub
emitter:go
Dominant language
TypeScript
Stars
27
Forks
90
Avg merge
1d 22h
Merged PRs (30d)
156

Description

## Description

The XML nested-model alias approach introduced in #5466 does not handle two cases:

1. Referencing the same XML-renamed model from multiple properties generates duplicate local alias declarations and invalid Go code.
2. An unwrapped array whose item name differs from the model XML root name serializes each item with the model root name instead of the array item name.

## Case 1: repeated references generate duplicate aliases

### TypeSpec

```tsp
import "@typespec/http";
import "@typespec/xml";

using Http;

@Xml.name("XmlAuthor")
model Author {
name: string;
}

@Xml.name("XmlLibrary")
model Library {
@Xml.name("primary")
primary: Author;

@Xml.name("secondary")
secondary: Author;
}
```

### Actual generated code

The marshaller generates the same local alias once per property:

```go
func (l Library) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
start.Name.Local = "XmlLibrary"
type alias Library
type author Author
type author Author
// ...
}
```

The generated module does not compile:

```text
author redeclared in this block
other declaration of author
```

### Expected behavior

The generated code should declare the methodless `Author` alias only once and reuse it for both fields, while preserving the property element names `primary` and `secondary`.

## Case 2: renamed unwrapped array items use the model root name

### TypeSpec

```tsp
import "@typespec/http";
import "@typespec/xml";

using Http;

@Xml.name("XmlAuthor")
model Author {
name: string;
}

model Library {
@Xml.unwrapped
@Xml.name("contributor")
contributors: Author[];
}
```

The generated field and marshaller retain the original `Author` element type:

```go
type Library struct {
Contributors []*Author `xml:"contributor"`
}

func (l Library) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
type alias Library
aux := &struct {
*alias
Contributors *[]*Author `xml:"contributor"`
}{
alias: (*alias)(&l),
}
// ...
}
```

Because `Author.MarshalXML` unconditionally applies the model root name:

```go
func (a Author) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {
start.Name.Local = "XmlAuthor"
// ...
}
```

this runtime example:

```go
name := "Ada"
data, err := xml.Marshal(Library{
Contributors: []*Author{{Name: &name}},
})
```

produces:

```xml
Ada
```

instead of:

```xml
Ada
```

### Expected behavior

Nested array items should preserve the item name supplied by the containing field, while direct `xml.Marshal(Author{...})` should continue to use `XmlAuthor` as the root name.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.