Comments (17)
Hi @Zekfad, thank you for your interest in this package. When the xml_serializable
package was created it was intended to match the behaviour of the json_serializable
package as closely as possible, however I agree that there is a lot more boilerplate required for XML. I will aim to further investigate the use of mixins in order to reduce the amount of boilerplate this week.
from dart-xml-serializable.
Hi @Zekfad, thank you again for your feature request. Having investigated further, I'm not sure how I feel about using a generic mixin and parser classes for the build...
and to...
methods, because in my opinion it trades off boilerplate code for slightly more complexity in the class being serialized with the inclusion of the mixin and the _parser
. I also think that the _parser
would need to be public if the intention is for the mixin to be exported from the package rather than the class being serialized. A potential alternative to reduce the amount of boilerplate required that I quite like might be to move the build...
and to...
methods into a generated extension on the class being serialized like this. I would be interested to know what you think of this proposal.
from dart-xml-serializable.
If you want to import a single class, you'll always need to explicitly import extension
Apologies, I'm having difficulty reproducing this locally, I've updated the branch with a sample that seems to work.
// This works.
import 'xml_serializable_example.dart';
// This does not.
// import 'xml_serializable_example.dart' show Book;
void main(List<String> arguments) {
// This works.
final book = Book();
book.toXmlElement();
// This does not.
// dynamic book = Book();
// book.toXmlElement();
}
EDIT: I see what you mean now, if only the Book
class is imported, then it won't also import the extensions, my bad!
from dart-xml-serializable.
I meant that following likely must be done by hand:
import 'xml_serializable_example.dart' show Book, BookXmlSerializableExtensions;
-> import 'xml_serializable_example.dart' show BookTest, BookTestXmlSerializableExtensions;
Notice BookTestXmlSerializableExtensions likely wont be renamed with refactor.
from dart-xml-serializable.
Whilst I agree that the approach using both a generic mixin and a generic abstract class (interface) offers benefits, in my opinion it introduces quite a bit of complexity that not everyone might be comfortable with. It would also require a major release of this package as it would be a breaking change. I have updated the branch, converting the extensions to mixins in order to overcome some of the obstacles that you mentioned such as imports and dynamics, whilst still keeping it fairly straightforward. In theory I think that it would still be possible for anyone desiring the extra benefits of an abstract class (interface) to add this to their project. I would be interested to know what you think of this approach instead of the extension based approach.
from dart-xml-serializable.
Hi @tnc1997, thanks for reply. This looks good to me, you can even add something like that:
import 'package:xml/xml.dart';
// Name subject to change
abstract class XmlSerializableClass {
void buildXmlChildren(
XmlBuilder builder, {
Map<String, String> namespaces = const {},
});
void buildXmlElement(
XmlBuilder builder, {
Map<String, String> namespaces = const {},
});
List<XmlAttribute> toXmlAttributes({
Map<String, String?> namespaces = const {},
});
List<XmlNode> toXmlChildren({
Map<String, String?> namespaces = const {},
});
XmlElement toXmlElement({
Map<String, String?> namespaces = const {},
});
}
And make changes to mixins like this:
// ...
mixin BookXmlSerializableMixin implements XmlSerializableClass {
// ...
After that, something like this will be possible:
final XmlSerializableClass book = Book();
book.buildXmlChildren(XmlBuilder());
That's indeed good solution, I think.
Note: I'm not so good at naming, so my versions are: XmlSerializableInterface
, XmlSerializableClass
, (already taken), XmlSerializable
XmlSerializableObject
.
from dart-xml-serializable.
That's indeed good solution, I think.
Happy days, in that case I will start work adding an option to use mixins instead of creating build...
and to...
methods in each class manually as per the example. In order to maintain backwards compatibility I will probably introduce this as opt-in initially. Thank you again for providing feedback and helping to architect the solution. I will keep this issue open until the changes have been released to https://pub.dev and comment here if there are any unforeseen issues during the development process.
from dart-xml-serializable.
but maybe a completely different API still would require major version bump rather than being an optional feature
I would like to avoid a completely different API ideally, because whilst possible it introduces a lot of maintenance overhead.
I'd really love to see a way of splitting classes (in my use case I'll have to write about 17 different elements)
Apologies if I'm misunderstanding, but if you're referring to the ability to create one class per file, then that is possible in the current version without an interface and I intend to retain this behaviour. I have updated the branch to show a code sample.
from dart-xml-serializable.
Any progress on this?
Hi @Zekfad, apologies but there hasn't been much progress yet. I'm currently in the process of making internal changes to the package in order to improve maintainability and support unit tests, before working on adding new features and functionality. That being said, I'm aiming to have a branch with mixin support available for you to test within the next couple of weeks if everything goes to plan.
from dart-xml-serializable.
Hello @tnc1997, thank for you reply. Your approach with extensions, looks good, but has two problems. If you want to import a single class, you'll always need to explicitly import extension (using example from your branch):
import 'xml_serializable_example.dart' show Book, BookXmlSerializableExtensions;
void main(List<String> arguments) async {
Book book = Book();
book.toXmlAttributes();
}
Notice, that without explicitly importing BookXmlSerializableExtensions
it wont work, also, you wont be able to easily rename the class with a single hit of refactor (F2 in most IDE), because BookXmlSerializableExtensions
has class name hardcoded within.
Also extensions doesn't work with dynamic, so this will fail:
dynamic book = Book();
book.toXmlAttributes();
NoSuchMethodError (NoSuchMethodError: Class 'Book' has no instance method 'toXmlAttributes'.
Receiver: Instance of 'Book'
Tried calling: toXmlAttributes())
Just aside from that, it would be nice to have some sort of interface (in terms of dart it is an abstract class, we can import and use implements
with it) for all serializable classed, but that's can be done on userland.
from dart-xml-serializable.
Adding more complete example:
Using mixins first 2 lines are ok, but the last aren't:
import 'xml_serializable_example.dart' show Book, BookXmlSerializableExtensions;
import 'package:xml/xml.dart';
import 'test.dart' show Person; // code from first post
void main(List<String> arguments) async {
dynamic person = Person();
person.buildXmlChildren(XmlBuilder());
dynamic book = Book();
book.buildXmlChildren(XmlBuilder());
}
Regarding _parser
, yes, it must be public parser
instead, otherwise we cant move XMLParserMixin
into different file, that's my mistake.
Because parser
is quite common name, we can make it something like xmlSerializableParserInstance
, to remove possible collisions.
from dart-xml-serializable.
you wont be able to easily rename the class with a single hit of refactor
In theory running build_runner
should handle leftover mentions of the old name in the same way as json_serializable
:
factory BookTest.fromXmlElement(XmlElement element) => _$BookFromXmlElement(element);
->
factory BookTest.fromXmlElement(XmlElement element) => _$BookTestFromXmlElement(element);
extension BookXmlSerializableExtensions on BookTest
->
extension BookTestXmlSerializableExtensions on BookTest
I think that this would be the same as in the parser approach where the parser _$PersonParser
contains the class name.
from dart-xml-serializable.
With little more changes here's additional benefit (generic interface):
AuxCode.dart (lib):
import 'package:xml/xml.dart';
typedef XMLSerializableClass<T> = XMLParserMixin<T, XMLSerializableParser<T>>;
abstract class XMLSerializableParser<T> {
const XMLSerializableParser();
T fromXmlElement(XmlElement element);
void buildXmlChildren(
T instance,
XmlBuilder builder, {
Map<String, String> namespaces = const {},
});
// and so on...
}
mixin XMLParserMixin<T, Parser extends XMLSerializableParser<T>> {
Parser get xmlSerializableParser;
void buildXmlChildren(
XmlBuilder builder, {
Map<String, String> namespaces = const {},
}) =>
xmlSerializableParser.buildXmlChildren(
this as T,
builder,
namespaces: namespaces,
);
// and so on...
}
person.dart:
import 'package:xml/xml.dart';
import 'AuxCode.dart';
part 'person.g.dart';
/// File code
class Person with XMLSerializableClass<Person> {
// or full form:
//class Person with XMLParserMixin<Person, _$PersonParser> {
static const _personParser = _$PersonParser();
// we can use directly generated class type, but also generic one
// to reduce dependency on generated names and less work when we rename class
@override
XMLSerializableParser<Person> get xmlSerializableParser => _personParser;
//_$PersonParser get xmlSerializableParser => _personParser;
String? name;
Person({
this.name,
});
factory Person.fromXmlElement(XmlElement element) =>
_personParser.fromXmlElement(element);
// Other methods are implemented via mixin
}
person.g.dart (supposed to be generated):
part of 'person.dart';
class _$PersonParser extends XMLSerializableParser<Person> {
const _$PersonParser() : super();
@override
Person fromXmlElement(XmlElement element) {
print('called fromXmlElement of _\$PersonParser');
return Person();
}
@override
void buildXmlChildren(
Person instance,
XmlBuilder builder, {
Map<String, String> namespaces = const {},
}) {
print('called buildXmlChildren of _\$PersonParser');
}
}
main.dart:
import 'package:xml/xml.dart';
import 'AuxCode.dart' show XMLSerializableClass;
import 'person.dart' show Person;
void main(List<String> arguments) async {
XMLSerializableClass<Person> person = Person();
person.buildXmlChildren(XmlBuilder());
someGenericFunctionWhichNeedXMLSerializableObject(person);
Person person2 = Person();
person2.buildXmlChildren(XmlBuilder());
someGenericFunctionWhichNeedXMLSerializableObject(person2);
}
void someGenericFunctionWhichNeedXMLSerializableObject<T extends XMLSerializableClass<T>>(T instance) {
print('Calling generic function with known interface');
instance.buildXmlChildren(XmlBuilder());
}
EDIT: Changed some file code, as it turns out we can use more generic approach to keep less generated names in main file.
from dart-xml-serializable.
@tnc1997, speaking of interface, because of it you can split classes across different files, because you can guarantee existence of required for in-depth generation methods.
In your example, we can extract all classes into separated files, (I guess this will need additional annotation, to mark properties which are external xml serializable classes) and inside of mixins you can assign such properties to interface type.
For example:
mixin BookXmlSerializableMixin implements XmlSerializableClass {
void buildXmlChildren(
XmlBuilder builder, {
Map<String, String> namespaces = const {},
}) {
// was: final title = (this as Book).title;
final XmlSerializableClass? title = (this as Book).title;
//...
So we'll have even better end user API, because it will be possible to split different classes into different files.
from dart-xml-serializable.
That's good to hear!
What about #13 (comment), I'd really love to see a way of splitting classes (in my use case I'll have to write about 17 different elements, it would be messy to put them all into a single file)?
That's of course not for me to decide, but maybe a completely different API still would require major version bump rather than being an optional feature. AFAIK pub.dev allows (not sure) to publish lower version, so it's possible to maintain old version from bugs within a separate branch, similar as npm has tags,
from dart-xml-serializable.
Any progress on this?
from dart-xml-serializable.
Hi @Zekfad, apologies for the delay, but I have some good news. There is now a preview branch available to try out the new mixin functionality. The branch can be found at feat/13, it would be great if you could give it a try and let me know of any issues.
dependencies:
xml_annotation:
git:
path: xml_annotation
ref: feat/13
url: https://github.com/tnc1997/dart-xml-serializable.git
dev_dependencies:
xml_serializable:
git:
path: xml_serializable
ref: feat/13
url: https://github.com/tnc1997/dart-xml-serializable.git
from dart-xml-serializable.
Related Issues (20)
- Only deserialize fields with an annotation in generate from XML element HOT 5
- Enum-Serializtion does not work anymore if split between multiple files HOT 1
- Reduce boilerplate code HOT 1
- Include beginner friendly docs HOT 1
- ignore attributes HOT 2
- How to describe a self closing optional element? HOT 3
- Add support for fields in superclasses HOT 8
- Add support for import prefixes HOT 3
- Asynchronous deserialization with Streams HOT 3
- Some methods not generated by builder HOT 3
- Dynamically choose names for XML-elements HOT 3
- feat: Add support for XML base64Binary type HOT 3
- Add support for serializing XML CDATA HOT 2
- Add support for custom converters HOT 3
- Upgrade the version constraint of analyzer HOT 1
- Add support for serializing elements using InnerXml HOT 7
- Better deserialization of empty elements / empty strings HOT 7
- Created List of single null class object but xml element is empty HOT 4
- Nullable fields annotated with XmlCDATA results in incorrectly generated source code HOT 1
- How to Write XML Element Names That Have a $ Sign HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dart-xml-serializable.