Line data Source code
1 : #include "JSONFeedParser.h"
2 :
3 : #include <QJsonDocument>
4 : #include <QJsonObject>
5 : #include <QJsonArray>
6 : #include <QDateTime>
7 :
8 : #include "RawFeed.h"
9 : #include "RawNews.h"
10 :
11 11 : std::unique_ptr<RawFeed> JSONFeedParser::parse(const QByteArray& data)
12 : {
13 11 : QJsonParseError parseError;
14 11 : QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
15 11 : if (parseError.error != QJsonParseError::NoError || !doc.isObject()) {
16 0 : return nullptr;
17 : }
18 :
19 11 : QJsonObject root = doc.object();
20 :
21 : // Validate version field.
22 11 : QString version = root.value("version").toString();
23 11 : if (!version.startsWith("https://jsonfeed.org/version/")) {
24 1 : return nullptr;
25 : }
26 :
27 10 : auto feed = std::make_unique<RawFeed>();
28 10 : feed->feedType = RawFeed::JSONFeed;
29 :
30 10 : feed->title = root.value("title").toString("");
31 10 : feed->subtitle = root.value("description").toString("");
32 :
33 10 : QString homePageUrl = root.value("home_page_url").toString();
34 10 : if (!homePageUrl.isEmpty()) {
35 8 : feed->siteURL = QUrl(homePageUrl);
36 : }
37 :
38 10 : QString feedUrl = root.value("feed_url").toString();
39 10 : if (!feedUrl.isEmpty()) {
40 5 : feed->url = QUrl(feedUrl);
41 : }
42 :
43 : // Prefer icon over favicon for the feed image.
44 10 : QString icon = root.value("icon").toString();
45 10 : if (icon.isEmpty()) {
46 5 : icon = root.value("favicon").toString();
47 : }
48 10 : if (!icon.isEmpty()) {
49 5 : feed->imageURL = QUrl(icon);
50 : }
51 :
52 : // Parse items.
53 10 : QJsonArray items = root.value("items").toArray();
54 32 : for (const QJsonValue& itemVal : items) {
55 22 : if (!itemVal.isObject()) {
56 0 : continue;
57 : }
58 :
59 22 : QJsonObject itemObj = itemVal.toObject();
60 :
61 : // Skip items without an id.
62 22 : QString id = itemObj.value("id").toString();
63 22 : if (id.isEmpty()) {
64 0 : continue;
65 : }
66 :
67 22 : auto news = std::make_shared<RawNews>();
68 22 : news->guid = id;
69 :
70 22 : QString itemUrl = itemObj.value("url").toString();
71 22 : if (!itemUrl.isEmpty()) {
72 21 : news->url = QUrl(itemUrl);
73 : }
74 :
75 22 : news->title = itemObj.value("title").toString();
76 :
77 : // content_html takes priority over content_text for the content field.
78 22 : QString contentHtml = itemObj.value("content_html").toString("");
79 22 : QString contentText = itemObj.value("content_text").toString("");
80 22 : if (!contentHtml.isEmpty()) {
81 12 : news->content = contentHtml;
82 10 : } else if (!contentText.isEmpty()) {
83 9 : news->content = contentText;
84 : }
85 :
86 : // summary => description; fall back to content_text if no summary.
87 22 : QString summary = itemObj.value("summary").toString("");
88 22 : if (!summary.isEmpty()) {
89 5 : news->description = summary;
90 17 : } else if (!contentText.isEmpty()) {
91 9 : news->description = contentText;
92 : }
93 :
94 : // Timestamp.
95 22 : QString datePublished = itemObj.value("date_published").toString();
96 22 : if (!datePublished.isEmpty()) {
97 20 : news->timestamp = QDateTime::fromString(datePublished, Qt::ISODate);
98 20 : if (news->timestamp.isValid()) {
99 20 : news->timestamp = news->timestamp.toUTC();
100 : }
101 : }
102 :
103 : // Image.
104 22 : QString image = itemObj.value("image").toString();
105 22 : if (!image.isEmpty()) {
106 5 : news->mediaImageURL = image;
107 : }
108 :
109 : // Author. Try v1.1 authors array first, then v1.0 author object.
110 22 : QJsonArray authors = itemObj.value("authors").toArray();
111 22 : if (!authors.isEmpty()) {
112 10 : QJsonObject firstAuthor = authors.first().toObject();
113 10 : news->author = firstAuthor.value("name").toString("");
114 10 : } else {
115 12 : QJsonObject authorObj = itemObj.value("author").toObject();
116 12 : if (!authorObj.isEmpty()) {
117 4 : news->author = authorObj.value("name").toString("");
118 : }
119 12 : }
120 :
121 22 : feed->items.append(news);
122 22 : }
123 :
124 10 : return feed;
125 11 : }
|