Skip to content

Commit 273ead3

Browse files
authored
fix: nullable converter types include if null (#62)
1 parent 5e818e4 commit 273ead3

19 files changed

+717
-132
lines changed

xml_serializable/lib/src/builder_generators/xml_converter_xml_element_builder_generator.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@ class XmlConverterXmlElementBuilderGenerator extends BuilderGenerator {
2121
/// The name of the converter.
2222
final String _converter;
2323

24+
/// If `false` (the default) then the type of the converter does not represent a nullable type.
25+
final bool _isConverterNullable;
26+
2427
const XmlConverterXmlElementBuilderGenerator(
2528
this._name,
2629
this._converter, {
2730
String? namespace,
2831
bool? isSelfClosing,
2932
bool? includeIfNull,
3033
bool isNullable = false,
34+
bool isConverterNullable = false,
3135
}) : _namespace = namespace,
3236
_isSelfClosing = isSelfClosing,
3337
_includeIfNull = includeIfNull,
34-
_isNullable = isNullable;
38+
_isNullable = isNullable,
39+
_isConverterNullable = isConverterNullable;
3540

3641
@override
3742
String generateBuilder(String expression, {String builder = 'builder'}) {
@@ -53,15 +58,15 @@ class XmlConverterXmlElementBuilderGenerator extends BuilderGenerator {
5358

5459
buffer.write(', nest: () { ');
5560

56-
if (_isNullable && _includeIfNull != false) {
61+
if (_isNullable && !_isConverterNullable && _includeIfNull != false) {
5762
buffer.write('if ($expression != null) { ');
5863
}
5964

6065
buffer.write(
6166
'const $_converter().buildXmlChildren($expression, $builder, namespaces: namespaces);',
6267
);
6368

64-
if (_isNullable && _includeIfNull != false) {
69+
if (_isNullable && !_isConverterNullable && _includeIfNull != false) {
6570
buffer.write(' }');
6671
}
6772

@@ -90,5 +95,6 @@ class NullableXmlConverterXmlElementBuilderGenerator
9095
isSelfClosing: isSelfClosing,
9196
includeIfNull: includeIfNull,
9297
isNullable: true,
98+
isConverterNullable: true,
9399
);
94100
}

xml_serializable/lib/src/constructor_generators/xml_converter_xml_element_constructor_generator.dart

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,22 @@ class XmlConverterXmlElementConstructorGenerator extends ConstructorGenerator {
2121
/// The name of the converter.
2222
final String _converter;
2323

24+
/// If `false` (the default) then the type of the converter does not represent a nullable type.
25+
final bool _isConverterNullable;
26+
2427
const XmlConverterXmlElementConstructorGenerator(
2528
this._name,
2629
this._converter, {
2730
String? namespace,
2831
bool? isSelfClosing,
2932
bool? includeIfNull,
3033
bool isNullable = false,
34+
bool isConverterNullable = false,
3135
}) : _namespace = namespace,
3236
_isSelfClosing = isSelfClosing,
3337
_includeIfNull = includeIfNull,
34-
_isNullable = isNullable;
38+
_isNullable = isNullable,
39+
_isConverterNullable = isConverterNullable;
3540

3641
@override
3742
String generateConstructor(String expression) {
@@ -49,29 +54,29 @@ class XmlConverterXmlElementConstructorGenerator extends ConstructorGenerator {
4954

5055
buffer.write('), ');
5156

52-
if (_isNullable && _includeIfNull != false) {
57+
if (_isNullable && !_isConverterNullable && _includeIfNull != false) {
5358
buffer.write('$expression != null ? ');
5459
}
5560

5661
buffer.write(
5762
'const $_converter().toXmlAttributes($expression, namespaces: namespaces)',
5863
);
5964

60-
if (_isNullable && _includeIfNull != false) {
65+
if (_isNullable && !_isConverterNullable && _includeIfNull != false) {
6166
buffer.write(' : []');
6267
}
6368

6469
buffer.write(', ');
6570

66-
if (_isNullable && _includeIfNull != false) {
71+
if (_isNullable && !_isConverterNullable && _includeIfNull != false) {
6772
buffer.write('$expression != null ? ');
6873
}
6974

7075
buffer.write(
7176
'const $_converter().toXmlChildren($expression, namespaces: namespaces)',
7277
);
7378

74-
if (_isNullable && _includeIfNull != false) {
79+
if (_isNullable && !_isConverterNullable && _includeIfNull != false) {
7580
buffer.write(' : []');
7681
}
7782

@@ -104,5 +109,6 @@ class NullableXmlConverterXmlElementConstructorGenerator
104109
isSelfClosing: isSelfClosing,
105110
includeIfNull: includeIfNull,
106111
isNullable: true,
112+
isConverterNullable: true,
107113
);
108114
}

xml_serializable/lib/src/extensions/element_extensions.dart

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,48 @@ extension ElementExtensions on Element {
7878
/// }
7979
/// ```
8080
///
81+
/// or as a converter that is contained within the annotation of the form `@XmlSerializable(converters: [...])` for example:
82+
///
83+
/// ```dart
84+
/// @XmlSerializable(converters: [TitleConverter()])
85+
/// class Book {
86+
/// Title? title;
87+
/// }
88+
///
8189
/// Throws a [StateError] if this element does not have an annotation that implements `XmlConverter` and can convert the [type]. Returns `null` if the value of the annotation could not be computed because of errors.
8290
DartObject? getXmlConverter({
8391
DartType? type,
8492
}) {
85-
for (var annotation in metadata) {
93+
for (final annotation in metadata) {
8694
if (annotation.isXmlConverter(type: type)) {
8795
return annotation.computeConstantValue();
8896
}
8997
}
9098

9199
final enclosingElement = this.enclosingElement;
92100
if (enclosingElement != null) {
93-
for (var annotation in metadata) {
101+
for (final annotation in enclosingElement.metadata) {
94102
if (annotation.isXmlConverter(type: type)) {
95103
return annotation.computeConstantValue();
96104
}
105+
106+
if (annotation.isXmlSerializable) {
107+
final converters = annotation
108+
.computeConstantValue()
109+
?.getField('converters')
110+
?.toListValue();
111+
if (converters != null) {
112+
for (final converter in converters) {
113+
if (converter.type!.isXmlAnnotationXmlConverter(type: type)) {
114+
return converter;
115+
}
116+
}
117+
}
118+
}
97119
}
98120
}
99121

100-
throw StateError("No element");
122+
throw StateError('No element');
101123
}
102124

103125
/// Gets the element that represents the class that can convert the [type] as an annotation on this element for example:

xml_serializable/lib/src/generator_factories/builder_generator_factory.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,22 @@ BuilderGenerator xmlElementBuilderGeneratorFactory(FieldElement element) {
9898
if (type is ParameterizedType &&
9999
(type.isDartCoreIterable || type.isDartCoreList || type.isDartCoreSet)) {
100100
final converterElement = element.getXmlConverterElement(type: type);
101-
if (converterElement != null) {
101+
if (converterElement is ClassElement) {
102102
return IterableBuilderGenerator(
103103
XmlConverterXmlElementBuilderGenerator(
104104
xmlElement.name ?? element.getEncodedFieldName(),
105-
converterElement.name!,
105+
converterElement.name,
106106
namespace: xmlElement.namespace,
107107
isSelfClosing: xmlElement.isSelfClosing,
108108
includeIfNull: xmlElement.includeIfNull,
109109
isNullable: type.typeArguments.single.isNullable,
110+
isConverterNullable: converterElement.thisType.allSupertypes.any(
111+
(supertype) =>
112+
supertype.element.library.identifier ==
113+
'package:xml_annotation/src/annotations/xml_converter.dart' &&
114+
supertype.element.name == 'XmlConverter' &&
115+
supertype.typeArguments.single.isNullable,
116+
),
110117
),
111118
isNullable: type.isNullable,
112119
);
@@ -135,14 +142,21 @@ BuilderGenerator xmlElementBuilderGeneratorFactory(FieldElement element) {
135142
}
136143
} else {
137144
final converterElement = element.getXmlConverterElement(type: type);
138-
if (converterElement != null) {
145+
if (converterElement is ClassElement) {
139146
return XmlConverterXmlElementBuilderGenerator(
140147
xmlElement.name ?? element.getEncodedFieldName(),
141-
converterElement.name!,
148+
converterElement.name,
142149
namespace: xmlElement.namespace,
143150
isSelfClosing: xmlElement.isSelfClosing,
144151
includeIfNull: xmlElement.includeIfNull,
145152
isNullable: type.isNullable,
153+
isConverterNullable: converterElement.thisType.allSupertypes.any(
154+
(supertype) =>
155+
supertype.element.library.identifier ==
156+
'package:xml_annotation/src/annotations/xml_converter.dart' &&
157+
supertype.element.name == 'XmlConverter' &&
158+
supertype.typeArguments.single.isNullable,
159+
),
146160
);
147161
} else if (type.element!.hasXmlSerializable) {
148162
return XmlSerializableXmlElementBuilderGenerator(

xml_serializable/lib/src/generator_factories/constructor_generator_factory.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,22 @@ ConstructorGenerator xmlElementConstructorGeneratorFactory(
104104
if (type is ParameterizedType &&
105105
(type.isDartCoreIterable || type.isDartCoreList || type.isDartCoreSet)) {
106106
final converterElement = element.getXmlConverterElement(type: type);
107-
if (converterElement != null) {
107+
if (converterElement is ClassElement) {
108108
return IterableConstructorGenerator(
109109
XmlConverterXmlElementConstructorGenerator(
110110
xmlElement.name ?? element.getEncodedFieldName(),
111-
converterElement.name!,
111+
converterElement.name,
112112
namespace: xmlElement.namespace,
113113
isSelfClosing: xmlElement.isSelfClosing,
114114
includeIfNull: xmlElement.includeIfNull,
115115
isNullable: type.typeArguments.single.isNullable,
116+
isConverterNullable: converterElement.thisType.allSupertypes.any(
117+
(supertype) =>
118+
supertype.element.library.identifier ==
119+
'package:xml_annotation/src/annotations/xml_converter.dart' &&
120+
supertype.element.name == 'XmlConverter' &&
121+
supertype.typeArguments.single.isNullable,
122+
),
116123
),
117124
isNullable: type.isNullable,
118125
);
@@ -141,14 +148,21 @@ ConstructorGenerator xmlElementConstructorGeneratorFactory(
141148
}
142149
} else {
143150
final converterElement = element.getXmlConverterElement(type: type);
144-
if (converterElement != null) {
151+
if (converterElement is ClassElement) {
145152
return XmlConverterXmlElementConstructorGenerator(
146153
xmlElement.name ?? element.getEncodedFieldName(),
147-
converterElement.name!,
154+
converterElement.name,
148155
namespace: xmlElement.namespace,
149156
isSelfClosing: xmlElement.isSelfClosing,
150157
includeIfNull: xmlElement.includeIfNull,
151158
isNullable: type.isNullable,
159+
isConverterNullable: converterElement.thisType.allSupertypes.any(
160+
(supertype) =>
161+
supertype.element.library.identifier ==
162+
'package:xml_annotation/src/annotations/xml_converter.dart' &&
163+
supertype.element.name == 'XmlConverter' &&
164+
supertype.typeArguments.single.isNullable,
165+
),
152166
);
153167
} else if (type.element!.hasXmlSerializable) {
154168
return XmlSerializableXmlElementConstructorGenerator(

xml_serializable/lib/src/generator_factories/getter_generator_factory.dart

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,19 @@ GetterGenerator xmlElementGetterGeneratorFactory(FieldElement element) {
8989
if (type is ParameterizedType &&
9090
(type.isDartCoreIterable || type.isDartCoreList || type.isDartCoreSet)) {
9191
final converterElement = element.getXmlConverterElement(type: type);
92-
if (converterElement != null) {
92+
if (converterElement is ClassElement) {
9393
return XmlConverterXmlElementIterableGetterGenerator(
9494
xmlElement.name ?? element.getEncodedFieldName(),
95-
converterElement.name!,
95+
converterElement.name,
9696
namespace: xmlElement.namespace,
9797
isNullable: type.isNullable,
98+
isConverterNullable: converterElement.thisType.allSupertypes.any(
99+
(supertype) =>
100+
supertype.element.library.identifier ==
101+
'package:xml_annotation/src/annotations/xml_converter.dart' &&
102+
supertype.element.name == 'XmlConverter' &&
103+
supertype.typeArguments.single.isNullable,
104+
),
98105
);
99106
} else if (type.typeArguments.single.element!.hasXmlSerializable) {
100107
return XmlSerializableXmlElementIterableGetterGenerator(
@@ -111,12 +118,19 @@ GetterGenerator xmlElementGetterGeneratorFactory(FieldElement element) {
111118
}
112119
} else {
113120
final converterElement = element.getXmlConverterElement(type: type);
114-
if (converterElement != null) {
121+
if (converterElement is ClassElement) {
115122
return XmlConverterXmlElementGetterGenerator(
116123
xmlElement.name ?? element.getEncodedFieldName(),
117-
converterElement.name!,
124+
converterElement.name,
118125
namespace: xmlElement.namespace,
119126
isNullable: type.isNullable,
127+
isConverterNullable: converterElement.thisType.allSupertypes.any(
128+
(supertype) =>
129+
supertype.element.library.identifier ==
130+
'package:xml_annotation/src/annotations/xml_converter.dart' &&
131+
supertype.element.name == 'XmlConverter' &&
132+
supertype.typeArguments.single.isNullable,
133+
),
120134
);
121135
} else if (type.element!.hasXmlSerializable) {
122136
return XmlSerializableXmlElementGetterGenerator(

xml_serializable/lib/src/getter_generators/xml_converter_xml_element_getter_generator.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ class XmlConverterXmlElementGetterGenerator extends XmlElementGetterGenerator {
44
/// The name of the converter.
55
final String _converter;
66

7+
/// If `false` (the default) then the type of the converter does not represent a nullable type.
8+
final bool _isConverterNullable;
9+
710
const XmlConverterXmlElementGetterGenerator(
811
String name,
912
this._converter, {
1013
String? namespace,
1114
bool isNullable = false,
12-
}) : super(
15+
bool isConverterNullable = false,
16+
}) : _isConverterNullable = isConverterNullable,
17+
super(
1318
name,
1419
namespace: namespace,
1520
isNullable: isNullable,
@@ -27,5 +32,6 @@ class NullableXmlConverterXmlElementGetterGenerator
2732
converter,
2833
namespace: namespace,
2934
isNullable: true,
35+
isConverterNullable: true,
3036
);
3137
}

xml_serializable/lib/src/getter_generators/xml_converter_xml_element_iterable_getter_generator.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ class XmlConverterXmlElementIterableGetterGenerator
55
/// The name of the converter.
66
final String _converter;
77

8+
/// If `false` (the default) then the type of the converter does not represent a nullable type.
9+
final bool _isConverterNullable;
10+
811
const XmlConverterXmlElementIterableGetterGenerator(
912
String name,
1013
this._converter, {
1114
String? namespace,
1215
bool isNullable = false,
13-
}) : super(
16+
bool isConverterNullable = false,
17+
}) : _isConverterNullable = isConverterNullable,
18+
super(
1419
name,
1520
namespace: namespace,
1621
isNullable: isNullable,
@@ -28,5 +33,6 @@ class NullableXmlConverterXmlElementIterableGetterGenerator
2833
converter,
2934
namespace: namespace,
3035
isNullable: true,
36+
isConverterNullable: true,
3137
);
3238
}

xml_serializable/lib/src/serializer_generators/xml_converter_xml_element_serializer_generator.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ class XmlConverterXmlElementSerializerGenerator extends SerializerGenerator {
77
/// If `false` (the default) then the type does not represent a nullable type.
88
final bool _isNullable;
99

10+
/// If `false` (the default) then the type of the converter does not represent a nullable type.
11+
final bool _isConverterNullable;
12+
1013
const XmlConverterXmlElementSerializerGenerator(
1114
this._converter, {
1215
bool isNullable = false,
13-
}) : _isNullable = isNullable;
16+
bool isConverterNullable = false,
17+
}) : _isNullable = isNullable,
18+
_isConverterNullable = isConverterNullable;
1419

1520
@override
1621
String generateSerializer(String expression) => expression;
@@ -40,5 +45,6 @@ class NullableXmlConverterXmlElementSerializerGenerator
4045
) : super(
4146
converter,
4247
isNullable: true,
48+
isConverterNullable: true,
4349
);
4450
}

0 commit comments

Comments
 (0)