KHealthCertificate

jsonld.cpp
1/*
2 * SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
3 * SPDX-License-Identifier: LGPL-2.0-or-later
4 */
5
6#include "jsonld_p.h"
7#include "rdf_p.h"
8
9#include <QJsonArray>
10#include <QJsonDocument>
11#include <QJsonObject>
12
13static bool readCurie(const QJsonObject::const_iterator it, JsonLdCurieMap &curieMap)
14{
15 const auto prefix = it.value().toString();
16 if (!prefix.startsWith(QLatin1String("http"))) {
17 return false;
18 }
19 constexpr const char allowdTrailingChars[] = ":/?#[]@";
20 if (std::count(std::begin(allowdTrailingChars), std::end(allowdTrailingChars), prefix.back().toLatin1()) == 0) {
21 return false;
22 }
23 curieMap.insert(it.key(), prefix);
24 return true;
25}
26
27static void resolveCurie(JsonLdProperty &prop, const JsonLdCurieMap &curieMap);
28
29static void resolveCurie(QString &str, const JsonLdCurieMap &curieMap)
30{
31 const auto idx = str.indexOf(QLatin1Char(':'));
32 if (idx < 0) {
33 return;
34 }
35 const auto prefix = QStringView(str).left(idx);
36 const auto fullPrefix = curieMap.value(prefix.toString());
37 if (fullPrefix.isEmpty()) {
38 return;
39 }
40 str = fullPrefix + QStringView(str).mid(idx + 1);
41}
42
43static void resolveCurie(JsonLdMetaType &mt, const JsonLdCurieMap &curieMap)
44{
45 resolveCurie(mt.qualifiedName, curieMap);
46 for (auto &prop : mt.properties) {
47 resolveCurie(prop, curieMap);
48 }
49}
50
51static void resolveCurie(JsonLdProperty &prop, const JsonLdCurieMap &curieMap)
52{
53 resolveCurie(prop.qualifiedName, curieMap);
54 resolveCurie(prop.type, curieMap);
55 resolveCurie(prop.metaType, curieMap);
56 resolveCurie(prop.prefix, curieMap);
57}
58
59JsonLdProperty JsonLdProperty::fromJson(const QString &name, const QJsonValue &value)
60{
61 JsonLdProperty prop;
62 if (name == QLatin1String("id")) {
63 return prop;
64 }
65 prop.name = name;
66 if (value.isString()) {
67 prop.qualifiedName = value.toString();
68 } else {
69 const auto obj = value.toObject();
70 prop.qualifiedName = obj.value(QLatin1String("@id")).toString();
71 prop.type = obj.value(QLatin1String("@type")).toString();
72 if (prop.type == QLatin1String("@vocab")) {
73 prop.prefix = prop.qualifiedName.left(prop.qualifiedName.indexOf(QLatin1Char(':')) + 1);
74 }
75 }
76 return prop;
77}
78
79
80void JsonLdMetaType::load(const QJsonObject &obj)
81{
82 qualifiedName = obj.value(QLatin1String("@id")).toString();
83 const auto context = obj.value(QLatin1String("@context")).toObject();
84 JsonLdCurieMap curieMap;
85 for (auto it = context.begin(); it != context.end(); ++it) {
86 if (it.value().isObject()) {
87 const auto propObj = it.value().toObject();
88 auto prop = JsonLdProperty::fromJson(it.key(), it.value());
89 if (propObj.contains(QLatin1String("@context"))) {
90 prop.metaType.load(propObj);
91 }
92 addProperty(std::move(prop));
93 }
94 else if (it.value().isString()) {
95 if (readCurie(it, curieMap)) {
96 continue;
97 }
98 addProperty(JsonLdProperty::fromJson(it.key(), it.value()));
99 }
100 }
101
102 resolveCurie(*this, curieMap);
103}
104
105void JsonLdMetaType::addProperty(JsonLdProperty &&property)
106{
107 if (property.name.isEmpty()) {
108 return;
109 }
110
111 auto it = std::lower_bound(properties.begin(), properties.end(), property, [](const auto &lhs, const auto &rhs) { return lhs.name < rhs.name; });
112 if (it == properties.end() || (*it).name != property.name) {
113 properties.insert(it, std::move(property));
114 }
115}
116
117
118void JsonLdContext::load(const QByteArray &contextData, const JsonLdDocumentLoader &loader)
119{
120 const auto doc = QJsonDocument::fromJson(contextData);
121 const auto context = doc.object().value(QLatin1String("@context"));
122 if (context.isObject()) {
123 load(context.toObject());
124 } else if (context.isArray()) {
125 for (const auto &c : context.toArray()) {
126 if (c.isObject()) {
127 load(c.toObject());
128 } else if (c.isString()) {
129 load(loader(c.toString()), loader);
130 }
131 }
132 }
133}
134
135void JsonLdContext::load(const QJsonObject &context)
136{
137 for (auto it = context.begin(); it != context.end(); ++it) {
138 if (it.value().isObject()) {
139 const auto subObj = it.value().toObject();
140 if (it.key().front().isUpper() || subObj.contains(QLatin1String("@context"))) {
141 JsonLdMetaType metaType;
142 metaType.name = it.key();
143 metaType.load(subObj);
144 metaTypes.push_back(std::move(metaType));
145 } else {
146 globalProperties.push_back(JsonLdProperty::fromJson(it.key(), it.value()));
147 }
148 }
149 else if (it.value().isString()) {
150 if (readCurie(it, curieMap)) {
151 continue;
152 }
153 if (it.key().front().isUpper()) {
154 JsonLdMetaType metaType;
155 metaType.name = it.key();
156 metaType.qualifiedName = it.value().toString();
157 metaTypes.push_back(std::move(metaType));
158 } else {
159 globalProperties.push_back(JsonLdProperty::fromJson(it.key(), it.value()));
160 }
161 }
162 }
163}
164
165void JsonLdContext::resolve()
166{
167 for (auto &mt : metaTypes) {
168 for (auto prop : globalProperties) { // copy is intentional!
169 mt.addProperty(std::move(prop));
170 }
171 }
172
173 for (auto &mt : metaTypes) {
174 resolveCurie(mt, curieMap);
175 }
176
177 std::sort(metaTypes.begin(), metaTypes.end(), [](const auto &lhs, const auto &rhs) {
178 return lhs.name < rhs.name;
179 });
180}
181
182JsonLdMetaType JsonLdContext::metaType(const QString &type) const
183{
184 const auto it = std::lower_bound(metaTypes.begin(), metaTypes.end(), type, [](const auto &lhs, const auto &rhs) {
185 return lhs.name < rhs;
186 });
187 if (it != metaTypes.end() && (*it).name == type) {
188 return *it;
189 }
190 return {};
191}
192
193void JsonLd::setDocumentLoader(const JsonLdDocumentLoader &loader)
194{
195 m_documentLoader = loader;
196}
197
198std::vector<Rdf::Quad> JsonLd::toRdf(const QJsonObject &obj) const
199{
200 std::vector<Rdf::Quad> quads;
201
202 // determine context
203 const auto contextVal = obj.value(QLatin1String("@context"));
204 JsonLdContext context;
205 if (contextVal.isArray()) {
206 for (const auto &contextV : contextVal.toArray()) {
207 context.load(m_documentLoader(contextV.toString()), m_documentLoader);
208 }
209 } else if (contextVal.isString()) {
210 context.load(m_documentLoader(contextVal.toString()), m_documentLoader);
211 }
212 context.resolve();
213
214 toRdfRecursive(context, obj, quads);
215 return quads;
216}
217
218Rdf::Term JsonLd::toRdfRecursive(const JsonLdContext &context, const QJsonObject &obj, std::vector<Rdf::Quad> &quads) const
219{
220 const auto id = idForObject(obj);
221
222 // find meta type for this object
223 const auto typeVal = obj.value(QLatin1String("type"));
224 if (typeVal.isArray()) {
225 for (const auto &typeV : typeVal.toArray()) {
226 toRdfRecursive(context, context.metaType(typeV.toString()), id, obj, quads);
227 }
228 } else if (typeVal.isString()) {
229 toRdfRecursive(context, context.metaType(typeVal.toString()), id, obj, quads);
230 }
231
232 return id;
233}
234
235void JsonLd::toRdfRecursive(const JsonLdContext &context, const JsonLdMetaType &mt, const Rdf::Term &id, const QJsonObject &obj, std::vector<Rdf::Quad> &quads) const
236{
237 if (mt.name.isEmpty() && mt.properties.empty()) { // meta type not found
238 return;
239 }
240
241 for (const auto &property : mt.properties) {
242 const auto val = obj.value(property.name);
243 if (val.isUndefined()) {
244 continue;
245 }
246
247 const auto createQuad = [&](const QJsonValue &value) {
248 Rdf::Quad quad;
249 quad.subject = id;
250 quad.predicate.value = property.qualifiedName;
251 quad.predicate.type = Rdf::Term::IRI;
252 if (value.isString()) {
253 quad.object.value = value.toString();
254 if (property.type == QLatin1String("@id")) {
255 quad.object.type = Rdf::Term::IRI;
256 } else if (property.qualifiedName == QLatin1String("@type")) {
257 quad.predicate.value = QStringLiteral("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
258 quad.object.type = Rdf::Term::IRI;
259 if (!property.metaType.qualifiedName.isEmpty()) {
260 quad.object.value = property.metaType.qualifiedName;
261 } else if (!mt.qualifiedName.isEmpty()) {
262 quad.object.value = mt.qualifiedName;
263 }
264 } else if (property.type == QLatin1String("@vocab")) {
265 quad.object.type = Rdf::Term::IRI;
266 quad.object.value = property.prefix + quad.object.value;
267 } else {
268 quad.object.type = Rdf::Term::Literal;
269 quad.object.literalType = property.type;
270 }
271 } else if (value.isObject()) {
272 if (property.metaType.properties.empty()) {
273 quad.object = toRdfRecursive(context, value.toObject(), quads);
274 } else {
275 const auto subId = idForObject(value.toObject());
276 toRdfRecursive(context, property.metaType, subId, value.toObject(), quads);
277 quad.object = subId;
278 }
279 } else if (value.isDouble()) {
280 quad.object.value = QString::number(value.toInt());
281 quad.object.type = Rdf::Term::Literal;
282 quad.object.literalType = QStringLiteral("http://www.w3.org/2001/XMLSchema#integer");
283 }
284 quads.push_back(std::move(quad));
285 };
286 if (val.isArray()) {
287 if (property.name == QLatin1String("type")) {
288 // we are already iterating over that one!
289 createQuad(mt.name);
290 } else {
291 for (const auto &i : val.toArray()) {
292 createQuad(i);
293 }
294 }
295 } else {
296 createQuad(val);
297 }
298 }
299}
300
301Rdf::Term JsonLd::idForObject(const QJsonObject &obj) const
302{
303 Rdf::Term id;
304 id.value = obj.value(QLatin1String("id")).toString();
305 if (id.value.isEmpty()) {
306 id.type = Rdf::Term::BlankNode;
307 id.value = QString::number(m_blankNodeCounter++);
308 } else {
309 id.type = Rdf::Term::IRI;
310 }
311 return id;
312}
QAction * load(const QObject *recvr, const char *slot, QObject *parent)
KGuiItem properties()
QString name(StandardShortcut id)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
iterator begin()
iterator end()
QJsonValue value(QLatin1StringView key) const const
bool isDouble() const const
bool isObject() const const
bool isString() const const
int toInt(int defaultValue) const const
QJsonObject toObject() const const
QString toString() const const
Type type() const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
QString number(double n, char format, int precision)
QStringView left(qsizetype length) const const
QStringView mid(qsizetype start, qsizetype length) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 10 2024 11:48:34 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.