-
Notifications
You must be signed in to change notification settings - Fork 227
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
Allow specifying xml namespace prefix #195
Changes from 4 commits
1e3c245
4241042
29c7677
4816aee
67abf93
f078b30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -221,8 +221,9 @@ following document: | |
``-``. Values for an ``xmlName`` adhere to the following ABNF. | ||
|
||
.. productionlist:: smithy | ||
xml_name :(ALPHA / "_") | ||
name :(ALPHA / "_") | ||
:*(ALPHA / DIGIT / "-" / "_") | ||
xml_name :name / (name ":" name) | ||
|
||
|
||
.. _xmlNamespace-trait: | ||
|
@@ -252,6 +253,9 @@ The ``xmlNamespace`` trait is an object that contains the following properties: | |
* - uri | ||
- ``string`` value containing a valid URI | ||
- **Required**. The namespace URI for scoping this XML element. | ||
* - prefix | ||
- ``string`` value | ||
- The prefix for elements from this namespace. | ||
|
||
Given the following structure definition, | ||
|
||
|
@@ -306,6 +310,71 @@ following document: | |
<bar>def</bar> | ||
</MyStructure> | ||
|
||
Given the following definition with a prefix: | ||
|
||
.. tabs:: | ||
|
||
.. code-tab:: smithy | ||
|
||
@xmlNamespace(uri: "http://foo.com", prefix: "bar") | ||
structure MyStructure { | ||
foo: String, | ||
@xmlName("baz:bar") | ||
bar: String, | ||
} | ||
|
||
.. code-tab:: json | ||
|
||
{ | ||
"smithy": "0.4.0", | ||
"smithy.example": { | ||
"shapes": { | ||
"MyStructure": { | ||
"type": "structure", | ||
"members": { | ||
"foo": { | ||
"target": "String" | ||
}, | ||
"bar": { | ||
"target": "String", | ||
"xmlName": "baz:bar" | ||
} | ||
}, | ||
"xmlNamespace": { | ||
"uri": "http://foo.com", | ||
"prefix": "baz" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
and the following values provided for ``MyStructure``, | ||
|
||
:: | ||
|
||
"foo" = "abc" | ||
"bar" = "def" | ||
|
||
the XML representation of the value would be serialized with the | ||
following document: | ||
|
||
.. code-block:: xml | ||
|
||
<MyStructure xmlns:baz="http//foo.com"> | ||
<foo>abc</foo> | ||
<baz:bar>def</baz:bar> | ||
</MyStructure> | ||
|
||
.. note:: | ||
|
||
Values for the ``prefix`` option must start with a letter (lower/upper | ||
case) or ``_``, followed by letters (lower/upper case), digits, ``_``, or | ||
``-``. Values for ``prefix`` adhere to the following ABNF. | ||
|
||
.. productionlist:: smithy | ||
prefix :(ALPHA / "_") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. xml_prefix? |
||
:*(ALPHA / DIGIT / "-" / "_") | ||
|
||
.. _xml-examples: | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,8 +17,10 @@ | |
|
||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import software.amazon.smithy.model.node.Node; | ||
import software.amazon.smithy.model.node.ObjectNode; | ||
import software.amazon.smithy.model.node.StringNode; | ||
import software.amazon.smithy.model.shapes.ShapeId; | ||
import software.amazon.smithy.utils.ListUtils; | ||
import software.amazon.smithy.utils.MapUtils; | ||
|
@@ -31,23 +33,32 @@ | |
public final class XmlNamespaceTrait extends AbstractTrait implements ToSmithyBuilder<XmlNamespaceTrait> { | ||
public static final ShapeId ID = ShapeId.from("smithy.api#xmlNamespace"); | ||
|
||
private static final List<String> XML_NAMESPACE_PROPERTIES = ListUtils.of("uri"); | ||
private static final String PREFIX = "prefix"; | ||
private static final String URI = "uri"; | ||
private static final List<String> XML_NAMESPACE_PROPERTIES = ListUtils.of(URI, PREFIX); | ||
|
||
private final String prefix; | ||
private final String uri; | ||
|
||
private XmlNamespaceTrait(Builder builder) { | ||
super(ID, builder.getSourceLocation()); | ||
uri = SmithyBuilder.requiredState("uri", builder.uri); | ||
prefix = builder.prefix; | ||
} | ||
|
||
public String getUri() { | ||
return uri; | ||
} | ||
|
||
public Optional<String> getPrefix() { | ||
return Optional.ofNullable(prefix); | ||
} | ||
|
||
@Override | ||
protected Node createNode() { | ||
return new ObjectNode(MapUtils.of(), getSourceLocation()) | ||
.withMember("uri", Node.from(uri)); | ||
.withMember(URI, Node.from(uri)) | ||
.withOptionalMember(PREFIX, getPrefix().map(Node::from)); | ||
} | ||
|
||
/** | ||
|
@@ -59,14 +70,19 @@ public static Builder builder() { | |
|
||
@Override | ||
public Builder toBuilder() { | ||
return builder().sourceLocation(getSourceLocation()).uri(uri); | ||
Builder traitBuilder = builder() | ||
.sourceLocation(getSourceLocation()) | ||
.uri(getUri()); | ||
getPrefix().ifPresent(traitBuilder::prefix); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that you should allow nulls for this value in the builder anyways (it's optional), you could just chain here and pass in the potentially null property of |
||
return traitBuilder; | ||
} | ||
|
||
/** | ||
* Builder used to create an XmlNamespace trait. | ||
*/ | ||
public static final class Builder extends AbstractTraitBuilder<XmlNamespaceTrait, Builder> { | ||
private String uri; | ||
private String prefix; | ||
|
||
private Builder() {} | ||
|
||
|
@@ -75,6 +91,11 @@ public Builder uri(String uri) { | |
return this; | ||
} | ||
|
||
public Builder prefix(String prefix) { | ||
this.prefix = Objects.requireNonNull(prefix); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should allow |
||
return this; | ||
} | ||
|
||
@Override | ||
public XmlNamespaceTrait build() { | ||
return new XmlNamespaceTrait(this); | ||
|
@@ -92,7 +113,8 @@ public XmlNamespaceTrait createTrait(ShapeId target, Node value) { | |
Builder builder = builder().sourceLocation(value); | ||
ObjectNode node = value.expectObjectNode(); | ||
node.warnIfAdditionalProperties(XML_NAMESPACE_PROPERTIES); | ||
builder.uri(node.expectStringMember("uri").getValue()); | ||
builder.uri(node.expectStringMember(URI).getValue()); | ||
node.getStringMember(PREFIX).map(StringNode::getValue).ifPresent(builder::prefix); | ||
return builder.build(); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -156,7 +156,7 @@ structure xmlFlattened {} | |
/// used in the model. | ||
@trait | ||
@tags(["diff.error.const"]) | ||
@pattern("^[a-zA-Z_][a-zA-Z_0-9-]*$") | ||
@pattern("^[a-zA-Z_][a-zA-Z_0-9-]*(:[a-zA-Z_][a-zA-Z_0-9-]*)?$") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern update should be reflected in the |
||
string xmlName | ||
|
||
/// Adds an xmlns namespace definition URI to an XML element. | ||
|
@@ -166,6 +166,9 @@ structure xmlNamespace { | |
/// The namespace URI for scoping this XML element. | ||
@required | ||
uri: NonEmptyString, | ||
/// The prefix for the given namespace. | ||
@pattern("^[a-zA-Z_][a-zA-Z_0-9-]*$") | ||
prefix: NonEmptyString, | ||
} | ||
|
||
@private | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[ERROR] ns.foo#InvalidName$a: Error validating trait `xmlName`: String value provided for `smithy.api#xmlName` must match regular expression: ^[a-zA-Z_][a-zA-Z_0-9-]*(:[a-zA-Z_][a-zA-Z_0-9-]*)?$ | TraitValue |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"smithy": "0.4.0", | ||
"ns.foo": { | ||
"shapes": { | ||
"ValidName1": { | ||
"type": "structure", | ||
"members": { | ||
"a": { | ||
"target": "smithy.api#String", | ||
"smithy.api#xmlName": "customname" | ||
} | ||
} | ||
}, | ||
"ValidName2": { | ||
"type": "structure", | ||
"members": { | ||
"a": { | ||
"target": "smithy.api#String", | ||
"smithy.api#xmlName": "customprefix:customname" | ||
} | ||
} | ||
}, | ||
"InvalidName": { | ||
"type": "structure", | ||
"members": { | ||
"a": { | ||
"target": "smithy.api#String", | ||
"smithy.api#xmlName": "too:many:colons" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we already use the name production anywhere else? I think this might break how we link to them across "smithy" production lists. Maybe
xml_name
andxml_identifier
?