| @@ -0,0 +1,61 @@ | |||
| import 'package:webfeed/util/helpers.dart'; | |||
| import 'package:xml/xml.dart'; | |||
| class DublinCore { | |||
| final String title; | |||
| final String description; | |||
| final String creator; | |||
| final String subject; | |||
| final String publisher; | |||
| final String contributor; | |||
| final String date; | |||
| final String type; | |||
| final String format; | |||
| final String identifier; | |||
| final String source; | |||
| final String language; | |||
| final String relation; | |||
| final String coverage; | |||
| final String rights; | |||
| DublinCore({ | |||
| this.title, | |||
| this.description, | |||
| this.creator, | |||
| this.subject, | |||
| this.publisher, | |||
| this.contributor, | |||
| this.date, | |||
| this.type, | |||
| this.format, | |||
| this.identifier, | |||
| this.source, | |||
| this.language, | |||
| this.relation, | |||
| this.coverage, | |||
| this.rights, | |||
| }); | |||
| factory DublinCore.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| return DublinCore( | |||
| title: findElementOrNull(element, "dc:title")?.text, | |||
| description: findElementOrNull(element, "dc:description")?.text, | |||
| creator: findElementOrNull(element, "dc:creator")?.text, | |||
| subject: findElementOrNull(element, "dc:subject")?.text, | |||
| publisher: findElementOrNull(element, "dc:publisher")?.text, | |||
| contributor: findElementOrNull(element, "dc:contributor")?.text, | |||
| date: findElementOrNull(element, "dc:date")?.text, | |||
| type: findElementOrNull(element, "dc:type")?.text, | |||
| format: findElementOrNull(element, "dc:format")?.text, | |||
| identifier: findElementOrNull(element, "dc:identifier")?.text, | |||
| source: findElementOrNull(element, "dc:source")?.text, | |||
| language: findElementOrNull(element, "dc:language")?.text, | |||
| relation: findElementOrNull(element, "dc:relation")?.text, | |||
| coverage: findElementOrNull(element, "dc:coverage")?.text, | |||
| rights: findElementOrNull(element, "dc:rights")?.text, | |||
| ); | |||
| } | |||
| } | |||
| @@ -1,11 +1,11 @@ | |||
| import 'package:xml/xml.dart'; | |||
| class RssCloud { | |||
| String domain; | |||
| String port; | |||
| String path; | |||
| String registerProcedure; | |||
| String protocol; | |||
| final String domain; | |||
| final String port; | |||
| final String path; | |||
| final String registerProcedure; | |||
| final String protocol; | |||
| RssCloud( | |||
| this.domain, | |||
| @@ -16,6 +16,9 @@ class RssCloud { | |||
| ); | |||
| factory RssCloud.parse(XmlElement node) { | |||
| if (node == null) { | |||
| return null; | |||
| } | |||
| var domain = node.getAttribute("domain"); | |||
| var port = node.getAttribute("port"); | |||
| var path = node.getAttribute("path"); | |||
| @@ -1,5 +1,6 @@ | |||
| import 'dart:core'; | |||
| import 'package:webfeed/domain/dublin_core/dublin_core.dart'; | |||
| import 'package:webfeed/domain/rss_category.dart'; | |||
| import 'package:webfeed/domain/rss_cloud.dart'; | |||
| import 'package:webfeed/domain/rss_image.dart'; | |||
| @@ -27,6 +28,7 @@ class RssFeed { | |||
| final String rating; | |||
| final String webMaster; | |||
| final int ttl; | |||
| final DublinCore dc; | |||
| RssFeed({ | |||
| this.title, | |||
| @@ -47,6 +49,7 @@ class RssFeed { | |||
| this.rating, | |||
| this.webMaster, | |||
| this.ttl, | |||
| this.dc, | |||
| }); | |||
| factory RssFeed.parse(String xmlString) { | |||
| @@ -55,105 +58,41 @@ class RssFeed { | |||
| try { | |||
| channelElement = document.findAllElements("channel").first; | |||
| } on StateError { | |||
| throw new ArgumentError("channel not found"); | |||
| throw ArgumentError("channel not found"); | |||
| } | |||
| var title = xmlGetString(channelElement, "title", strict: false); | |||
| var description = | |||
| xmlGetString(channelElement, "description", strict: false); | |||
| var link = xmlGetString(channelElement, "link", strict: false); | |||
| var feeds = channelElement.findElements("item").map((element) { | |||
| return new RssItem.parse(element); | |||
| }).toList(); | |||
| RssImage image; | |||
| try { | |||
| image = new RssImage.parse(channelElement.findElements("image").first); | |||
| } on StateError {} | |||
| RssCloud cloud; | |||
| try { | |||
| cloud = new RssCloud.parse(channelElement.findElements("cloud").first); | |||
| } on StateError {} | |||
| var categories = channelElement.findElements("category").map((element) { | |||
| return new RssCategory.parse(element); | |||
| }).toList(); | |||
| var skipDays = new List<String>(); | |||
| var skipDaysNodes = channelElement.findElements("skipDays"); | |||
| if (skipDaysNodes.isNotEmpty) { | |||
| skipDays = skipDaysNodes.first.findAllElements("day").map((element) { | |||
| return RssFeed( | |||
| title: findElementOrNull(channelElement, "title")?.text, | |||
| description: findElementOrNull(channelElement, "description")?.text, | |||
| link: findElementOrNull(channelElement, "link")?.text, | |||
| items: channelElement.findElements("item").map((element) { | |||
| return RssItem.parse(element); | |||
| }).toList(), | |||
| image: RssImage.parse(findElementOrNull(channelElement, "image")), | |||
| cloud: RssCloud.parse(findElementOrNull(channelElement, "cloud")), | |||
| categories: channelElement.findElements("category").map((element) { | |||
| return RssCategory.parse(element); | |||
| }).toList(), | |||
| skipDays: findElementOrNull(channelElement, "skipDays") | |||
| ?.findAllElements("day") | |||
| ?.map((element) { | |||
| return element.text; | |||
| }).toList(); | |||
| } | |||
| var skipHours = new List<int>(); | |||
| var skipHoursNodes = channelElement.findElements("skipHours"); | |||
| if (skipHoursNodes.isNotEmpty) { | |||
| skipHours = skipHoursNodes.first.findAllElements("hour").map((element) { | |||
| try { | |||
| return int.parse(element.text); | |||
| } on FormatException { | |||
| return null; | |||
| } | |||
| }).toList(); | |||
| } | |||
| var lastBuildDate = | |||
| xmlGetString(channelElement, "lastBuildDate", strict: false); | |||
| var language = xmlGetString(channelElement, "language", strict: false); | |||
| var generator = xmlGetString(channelElement, "generator", strict: false); | |||
| var copyright = xmlGetString(channelElement, "copyright", strict: false); | |||
| var docs = xmlGetString(channelElement, "docs", strict: false); | |||
| var managingEditor = | |||
| xmlGetString(channelElement, "managingEditor", strict: false); | |||
| var rating = xmlGetString(channelElement, "rating", strict: false); | |||
| var webMaster = xmlGetString(channelElement, "webMaster", strict: false); | |||
| var ttl = xmlGetInt(channelElement, "ttl", strict: false); | |||
| return new RssFeed( | |||
| title: title, | |||
| description: description, | |||
| link: link, | |||
| items: feeds, | |||
| image: image, | |||
| cloud: cloud, | |||
| categories: categories, | |||
| skipDays: skipDays, | |||
| skipHours: skipHours, | |||
| lastBuildDate: lastBuildDate, | |||
| language: language, | |||
| generator: generator, | |||
| copyright: copyright, | |||
| docs: docs, | |||
| managingEditor: managingEditor, | |||
| rating: rating, | |||
| webMaster: webMaster, | |||
| ttl: ttl, | |||
| })?.toList(), | |||
| skipHours: findElementOrNull(channelElement, "skipHours") | |||
| ?.findAllElements("hour") | |||
| ?.map((element) { | |||
| return int.tryParse(element.text ?? "0"); | |||
| })?.toList(), | |||
| lastBuildDate: findElementOrNull(channelElement, "lastBuildDate")?.text, | |||
| language: findElementOrNull(channelElement, "language")?.text, | |||
| generator: findElementOrNull(channelElement, "generator")?.text, | |||
| copyright: findElementOrNull(channelElement, "copyright")?.text, | |||
| docs: findElementOrNull(channelElement, "docs")?.text, | |||
| managingEditor: findElementOrNull(channelElement, "managingEditor")?.text, | |||
| rating: findElementOrNull(channelElement, "rating")?.text, | |||
| webMaster: findElementOrNull(channelElement, "webMaster")?.text, | |||
| ttl: int.tryParse(findElementOrNull(channelElement, "ttl")?.text ?? "0"), | |||
| dc: DublinCore.parse(channelElement), | |||
| ); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| title: $title | |||
| description: $description | |||
| link: $link | |||
| items: $items | |||
| image: $image | |||
| cloud: $cloud | |||
| categories: $categories | |||
| skipDays: $skipDays | |||
| skipHours: $skipHours | |||
| lastBuildDate: $lastBuildDate | |||
| language: $language | |||
| generator: $generator | |||
| copyright: $copyright | |||
| docs: $docs | |||
| managingEditor: $managingEditor | |||
| rating: $rating | |||
| webMaster: $webMaster | |||
| ttl: $ttl | |||
| '''; | |||
| } | |||
| } | |||
| @@ -9,6 +9,9 @@ class RssImage { | |||
| RssImage(this.title, this.url, this.link); | |||
| factory RssImage.parse(XmlElement element) { | |||
| if (element == null) { | |||
| return null; | |||
| } | |||
| var title = xmlGetString(element, "title", strict: false); | |||
| var url = xmlGetString(element, "url", strict: false); | |||
| var link = xmlGetString(element, "link", strict: false); | |||
| @@ -1,3 +1,4 @@ | |||
| import 'package:webfeed/domain/dublin_core/dublin_core.dart'; | |||
| import 'package:webfeed/domain/media/media.dart'; | |||
| import 'package:webfeed/domain/rss_category.dart'; | |||
| import 'package:webfeed/domain/rss_content.dart'; | |||
| @@ -21,6 +22,7 @@ class RssItem { | |||
| final RssContent content; | |||
| final Media media; | |||
| final RssEnclosure enclosure; | |||
| final DublinCore dc; | |||
| RssItem({ | |||
| this.title, | |||
| @@ -35,15 +37,16 @@ class RssItem { | |||
| this.content, | |||
| this.media, | |||
| this.enclosure, | |||
| this.dc, | |||
| }); | |||
| factory RssItem.parse(XmlElement element) { | |||
| return new RssItem( | |||
| return RssItem( | |||
| title: findElementOrNull(element, "title")?.text, | |||
| description: findElementOrNull(element, "description")?.text, | |||
| link: findElementOrNull(element, "link")?.text, | |||
| categories: element.findElements("category").map((element) { | |||
| return new RssCategory.parse(element); | |||
| return RssCategory.parse(element); | |||
| }).toList(), | |||
| guid: findElementOrNull(element, "guid")?.text, | |||
| pubDate: findElementOrNull(element, "pubDate")?.text, | |||
| @@ -53,21 +56,7 @@ class RssItem { | |||
| content: RssContent.parse(findElementOrNull(element, "content:encoded")), | |||
| media: Media.parse(element), | |||
| enclosure: RssEnclosure.parse(findElementOrNull(element, "enclosure")), | |||
| dc: DublinCore.parse(element), | |||
| ); | |||
| } | |||
| @override | |||
| String toString() { | |||
| return ''' | |||
| title: $title | |||
| description: $description | |||
| link: $link | |||
| guid: $guid | |||
| pubDate: $pubDate | |||
| author: $author | |||
| comments: $comments | |||
| source: $source | |||
| content: $content | |||
| '''; | |||
| } | |||
| } | |||
| @@ -213,4 +213,41 @@ void main() { | |||
| expect(item.media.scenes.first.startTime, "00:15"); | |||
| expect(item.media.scenes.first.endTime, "00:45"); | |||
| }); | |||
| test("parse RSS-DC.xml", (){ | |||
| var xmlString = File("test/xml/RSS-DC.xml").readAsStringSync(); | |||
| var feed = RssFeed.parse(xmlString); | |||
| expect(feed.dc.title, "title"); | |||
| expect(feed.dc.creator, "creator"); | |||
| expect(feed.dc.subject, "subject"); | |||
| expect(feed.dc.description, "description"); | |||
| expect(feed.dc.publisher, "publisher"); | |||
| expect(feed.dc.contributor, "contributor"); | |||
| expect(feed.dc.date, "2000-01-01T12:00+00:00"); | |||
| expect(feed.dc.type, "type"); | |||
| expect(feed.dc.format, "format"); | |||
| expect(feed.dc.identifier, "identifier"); | |||
| expect(feed.dc.source, "source"); | |||
| expect(feed.dc.language, "language"); | |||
| expect(feed.dc.relation, "relation"); | |||
| expect(feed.dc.coverage, "coverage"); | |||
| expect(feed.dc.rights, "rights"); | |||
| expect(feed.items.first.dc.title, "title"); | |||
| expect(feed.items.first.dc.creator, "creator"); | |||
| expect(feed.items.first.dc.subject, "subject"); | |||
| expect(feed.items.first.dc.description, "description"); | |||
| expect(feed.items.first.dc.publisher, "publisher"); | |||
| expect(feed.items.first.dc.contributor, "contributor"); | |||
| expect(feed.items.first.dc.date, "2000-01-01T12:00+00:00"); | |||
| expect(feed.items.first.dc.type, "type"); | |||
| expect(feed.items.first.dc.format, "format"); | |||
| expect(feed.items.first.dc.identifier, "identifier"); | |||
| expect(feed.items.first.dc.source, "source"); | |||
| expect(feed.items.first.dc.language, "language"); | |||
| expect(feed.items.first.dc.relation, "relation"); | |||
| expect(feed.items.first.dc.coverage, "coverage"); | |||
| expect(feed.items.first.dc.rights, "rights"); | |||
| }); | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <rss version="2.0" | |||
| xmlns:dc="http://purl.org/dc/elements/1.1/"> | |||
| <channel> | |||
| <dc:title>title</dc:title> | |||
| <dc:creator>creator</dc:creator> | |||
| <dc:subject>subject</dc:subject> | |||
| <dc:description>description</dc:description> | |||
| <dc:publisher>publisher</dc:publisher> | |||
| <dc:contributor>contributor</dc:contributor> | |||
| <dc:date>2000-01-01T12:00+00:00</dc:date> | |||
| <dc:type>type</dc:type> | |||
| <dc:format>format</dc:format> | |||
| <dc:identifier>identifier</dc:identifier> | |||
| <dc:source>source</dc:source> | |||
| <dc:language>language</dc:language> | |||
| <dc:relation>relation</dc:relation> | |||
| <dc:coverage>coverage</dc:coverage> | |||
| <dc:rights>rights</dc:rights> | |||
| <item> | |||
| <dc:title>title</dc:title> | |||
| <dc:creator>creator</dc:creator> | |||
| <dc:subject>subject</dc:subject> | |||
| <dc:description>description</dc:description> | |||
| <dc:publisher>publisher</dc:publisher> | |||
| <dc:contributor>contributor</dc:contributor> | |||
| <dc:date>2000-01-01T12:00+00:00</dc:date> | |||
| <dc:type>type</dc:type> | |||
| <dc:format>format</dc:format> | |||
| <dc:identifier>identifier</dc:identifier> | |||
| <dc:source>source</dc:source> | |||
| <dc:language>language</dc:language> | |||
| <dc:relation>relation</dc:relation> | |||
| <dc:coverage>coverage</dc:coverage> | |||
| <dc:rights>rights</dc:rights> | |||
| </item> | |||
| </channel> | |||
| </rss> | |||