LCOV - code coverage report
Current view: top level - lib/FangFeedParser - JSONFeedParser.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 95.7 % 70 67
Test Date: 2026-04-19 00:35:54 Functions: 100.0 % 1 1

            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 : }
        

Generated by: LCOV version 2.0-1