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

Update to create as well as consume Atom feeds #25

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,28 @@
[![Build Status](https://travis-ci.org/witochandra/webfeed.svg?branch=master)](https://travis-ci.org/witochandra/webfeed)
[![Pub](https://img.shields.io/pub/v/webfeed.svg)](https://pub.dartlang.org/packages/webfeed)

A dart package for parsing RSS and Atom feed.
A dart package for parsing and generating RSS and Atom feeds.

### Features

- [x] RSS
- [x] Atom
- [x] Namespaces
- [x] Media RSS
- [x] Dublin Core
- [x] Parsing
- [x] RSS
- [x] Atom
- [x] Namespaces
- [x] Media RSS
- [x] Dublin Core
- [ ] Generating
- [ ] RSS
- [x] Atom
- [ ] Namespaces
- [ ] Media RSS
- [ ] Dublin Core

### Installing

Add this line into your `pubspec.yaml`
```
webfeed: ^0.4.2
webfeed: ^0.5.0
```

Import the package into your dart code using:
Expand All @@ -27,10 +34,16 @@ import 'package:webfeed/webfeed.dart';

### Example

To parse string into `RssFeed` object use:
To parse string into an object use:
```dart
var rssFeed = RssFeed.parse(xmlString); // for parsing RSS feed
var atomFeed = AtomFeed.parse(xmlString); // for parsing Atom feed
```
var rssFeed = new RssFeed.parse(xmlString); // for parsing RSS feed
var atomFeed = new AtomFeed.parse(xmlString); // for parsing Atom feed

To generate string from an object use:
```dart
var atomFeed = AtomFeed(id: Uri.parse('urn:42'), title: 'a title', ...); // for creating Atom feed
var xmlString = atomFeed.toXml().toXmlString(); // for creating XML string for Atom feed
```

### Preview
Expand Down Expand Up @@ -105,6 +118,10 @@ item.rights
item.media
```

## Contributors
- Wito Chandra <[email protected]> (author)
- Chris Sells <[email protected]> (provided XML generation from Atom OM)

## License

WebFeed is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
3 changes: 0 additions & 3 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
analyzer:
language:
enablePreviewDart2: true
strong-mode: true
errors:
unused_import: error
unused_local_variable: error
Expand Down
24 changes: 8 additions & 16 deletions example/main.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import 'package:http/http.dart' as http;
import 'package:webfeed/webfeed.dart';

void main() {
var client = new http.Client();
void main() async {
var client = http.Client();

// RSS feed
client.get("https://developer.apple.com/news/releases/rss/releases.rss").then((response) {
return response.body;
}).then((bodyString) {
var channel = new RssFeed.parse(bodyString);
print(channel);
return channel;
});
var rssresp = await client.get("https://developer.apple.com/news/releases/rss/releases.rss");
var channel = RssFeed.parse(rssresp.body);
print(channel);

// Atom feed
client.get("https://www.theverge.com/rss/index.xml").then((response) {
return response.body;
}).then((bodyString) {
var feed = new AtomFeed.parse(bodyString);
print(feed);
return feed;
});
var atomresp = await client.get("https://www.theverge.com/rss/index.xml");
var feed = AtomFeed.parse(atomresp.body);
print(feed.toXml().toXmlString(pretty: true, indent: ' '));
}
23 changes: 17 additions & 6 deletions lib/domain/atom_category.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ class AtomCategory {
final String scheme;
final String label;

AtomCategory(this.term, this.scheme, this.label);
AtomCategory({
this.term,
this.scheme,
this.label,
});

factory AtomCategory.parse(XmlElement element) {
var term = element.getAttribute("term");
var scheme = element.getAttribute("scheme");
var label = element.getAttribute("label");
return AtomCategory(term, scheme, label);
factory AtomCategory.parse(XmlElement element) => AtomCategory(
term: element.getAttribute("term"),
scheme: element.getAttribute("scheme"),
label: element.getAttribute("label"),
);

void build(XmlBuilder b) {
b.element('category', nest: () {
if (term != null) b.attribute('term', term);
if (scheme != null) b.attribute('scheme', scheme);
if (label != null) b.attribute('label', label);
});
}
}
24 changes: 24 additions & 0 deletions lib/domain/atom_content.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:xml/xml.dart';

class AtomContent {
String type;
String text;

AtomContent({
this.type,
this.text,
});

factory AtomContent.parse(XmlElement element) {
if (element == null) return null;
return AtomContent(
type: element.getAttribute('type'),
text: element.text,
);
}

void build(XmlBuilder b, String kind) => b.element(kind, nest: () {
if (type != null) b.attribute('type', type);
if (text != null) b.text(text);
});
}
84 changes: 51 additions & 33 deletions lib/domain/atom_feed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,24 @@ import 'package:webfeed/util/helpers.dart';
import 'package:xml/xml.dart';

class AtomFeed {
final String id;
final Uri id;
final String title;
final String updated;
final DateTime updated;
final List<AtomItem> items;

final List<AtomLink> links;
final List<AtomPerson> authors;
final List<AtomPerson> contributors;
final List<AtomCategory> categories;
final AtomGenerator generator;
final String icon;
final String logo;
final Uri icon;
final Uri logo;
final String rights;
final String subtitle;

AtomFeed({
this.id,
this.title,
this.updated,
updated,
this.items,
this.links,
this.authors,
Expand All @@ -36,42 +35,61 @@ class AtomFeed {
this.logo,
this.rights,
this.subtitle,
});
}) : this.updated = updated ?? DateTime.now(); // default value

factory AtomFeed.parse(String xmlString) {
var document = parse(xmlString);
XmlElement feedElement;

try {
var document = parse(xmlString);
feedElement = document.findElements("feed").first;
} on StateError {
throw new ArgumentError("feed not found");
throw ArgumentError("feed not found");
}

return AtomFeed(
id: findElementOrNull(feedElement, "id")?.text,
title: findElementOrNull(feedElement, "title")?.text,
updated: findElementOrNull(feedElement, "updated")?.text,
items: feedElement.findElements("entry").map((element) {
return AtomItem.parse(element);
}).toList(),
links: feedElement.findElements("link").map((element) {
return AtomLink.parse(element);
}).toList(),
authors: feedElement.findElements("author").map((element) {
return AtomPerson.parse(element);
}).toList(),
contributors: feedElement.findElements("contributor").map((element) {
return AtomPerson.parse(element);
}).toList(),
categories: feedElement.findElements("category").map((element) {
return AtomCategory.parse(element);
}).toList(),
generator:
AtomGenerator.parse(findElementOrNull(feedElement, "generator")),
icon: findElementOrNull(feedElement, "icon")?.text,
logo: findElementOrNull(feedElement, "logo")?.text,
rights: findElementOrNull(feedElement, "rights")?.text,
subtitle: findElementOrNull(feedElement, "subtitle")?.text,
id: parseUriLiteral(feedElement, "id"),
title: parseTextLiteral(feedElement, "title"),
updated: parseDateTimeLiteral(feedElement, "updated"),
items: feedElement.findElements("entry").map((e) => AtomItem.parse(e)).toList(),
links: feedElement.findElements("link").map((e) => AtomLink.parse(e)).toList(),
authors: feedElement.findElements("author").map((e) => AtomPerson.parse(e)).toList(),
contributors: feedElement.findElements("contributor").map((e) => AtomPerson.parse(e)).toList(),
categories: feedElement.findElements("category").map((e) => AtomCategory.parse(e)).toList(),
generator: AtomGenerator.parse(findElementOrNull(feedElement, "generator")),
icon: parseUriLiteral(feedElement, "icon"),
logo: parseUriLiteral(feedElement, "logo"),
rights: parseTextLiteral(feedElement, "rights"),
subtitle: parseTextLiteral(feedElement, "subtitle"),
);
}

XmlDocument toXml() {
var doc = parse('<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"></feed>');
var b = XmlBuilder();
build(b);
var feed = doc.findAllElements('feed').first;
b.build().children.forEach((c) => feed.children.add(c.copy()));
return doc;
}

void build(XmlBuilder b) {
if (id == null) throw Exception('must have an id');

b.element('id', nest: () => b.text(id));
b.element('title', nest: () => title == null ? '' : b.text(title));
b.element('updated', nest: () => b.text(updated.toUtc().toIso8601String()));

if (links != null) links.forEach((l) => l.build(b));
if (authors != null) authors.forEach((a) => a.build(b, 'author'));
if (contributors != null) contributors.forEach((c) => c.build(b, 'contributor'));
if (categories != null) categories.forEach((c) => c.build(b));
if (generator != null) generator.build(b);

if (icon != null) b.element('icon', nest: () => b.text(icon));
if (logo != null) b.element('logo', nest: () => b.text(logo));
if (subtitle != null) b.element('subtitle', nest: () => b.text(subtitle));

if (items != null) items.forEach((i) => i.build(b));
}
}
29 changes: 21 additions & 8 deletions lib/domain/atom_generator.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
import 'package:xml/xml.dart';

class AtomGenerator {
final String uri;
final Uri uri;
final String version;
final String value;

AtomGenerator(this.uri, this.version, this.value);
AtomGenerator({
this.uri,
this.version,
this.value,
});

factory AtomGenerator.parse(XmlElement element) {
if (element == null) {
return null;
}
if (element == null) return null;
var uri = element.getAttribute("uri");
var version = element.getAttribute("version");
var value = element.text;
return new AtomGenerator(uri, version, value);
return AtomGenerator(
uri: uri == null ? null : Uri.parse(uri),
version: element.getAttribute("version"),
value: element.text,
);
}

void build(XmlBuilder b) {
// <generator uri="http://foo.bar.news/generator" version="1.0">Foo bar generator</generator>
b.element('generator', nest: () {
if (uri != null) b.attribute('uri', uri);
if (version != null) b.attribute('version', version);
if (value != null) b.text(value);
});
}
}
Loading