KD SOAP API Documentation  2.1
KDSoapValue.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Soap project.
4 **
5 ** SPDX-FileCopyrightText: 2010-2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 #include "KDSoapValue.h"
11 #include "KDDateTime.h"
12 #include "KDSoapNamespaceManager.h"
14 #include <QDateTime>
15 #include <QDebug>
16 #include <QStringList>
17 #include <QUrl>
18 
19 class KDSoapValue::Private : public QSharedData
20 {
21 public:
22  Private()
23  : m_qualified(false)
24  , m_nillable(false)
25  {
26  }
27  Private(const QString &n, const QVariant &v, const QString &typeNameSpace, const QString &typeName)
28  : m_name(n)
29  , m_value(v)
30  , m_typeNamespace(typeNameSpace)
31  , m_typeName(typeName)
32  , m_qualified(false)
33  , m_nillable(false)
34  {
35  }
36 
37  QString m_name;
38  QString m_nameNamespace;
39  QVariant m_value;
40  QString m_typeNamespace;
41  QString m_typeName;
42  KDSoapValueList m_childValues;
43  bool m_qualified;
44  bool m_nillable;
45  QXmlStreamNamespaceDeclarations m_environmentNamespaceDeclarations;
46  QXmlStreamNamespaceDeclarations m_localNamespaceDeclarations;
47 };
48 
49 uint qHash(const KDSoapValue &value)
50 {
51  return qHash(value.name());
52 }
53 
55  : d(new Private)
56 {
57 }
58 
59 KDSoapValue::KDSoapValue(const QString &n, const QVariant &v, const QString &typeNameSpace, const QString &typeName)
60  : d(new Private(n, v, typeNameSpace, typeName))
61 {
62 }
63 
64 KDSoapValue::KDSoapValue(const QString &n, const KDSoapValueList &children, const QString &typeNameSpace, const QString &typeName)
65  : d(new Private(n, QVariant(), typeNameSpace, typeName))
66 {
67  d->m_childValues = children;
68 }
69 
71 {
72 }
73 
75  : d(other.d)
76 {
77 }
78 
79 bool KDSoapValue::isNull() const
80 {
81  return d->m_name.isEmpty() && isNil();
82 }
83 
84 bool KDSoapValue::isNil() const
85 {
86  return d->m_value.isNull() && d->m_childValues.isEmpty() && d->m_childValues.attributes().isEmpty();
87 }
88 
89 void KDSoapValue::setNillable(bool nillable)
90 {
91  d->m_nillable = nillable;
92 }
93 
94 QString KDSoapValue::name() const
95 {
96  return d->m_name;
97 }
98 
99 void KDSoapValue::setName(const QString &name)
100 {
101  d->m_name = name;
102 }
103 
104 QVariant KDSoapValue::value() const
105 {
106  return d->m_value;
107 }
108 
109 void KDSoapValue::setValue(const QVariant &value)
110 {
111  d->m_value = value;
112 }
113 
115 {
116  return d->m_qualified;
117 }
118 
119 void KDSoapValue::setQualified(bool qualified)
120 {
121  d->m_qualified = qualified;
122 }
123 
124 void KDSoapValue::setNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &namespaceDeclarations)
125 {
126  d->m_localNamespaceDeclarations = namespaceDeclarations;
127 }
128 
129 void KDSoapValue::addNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &namespaceDeclaration)
130 {
131  d->m_localNamespaceDeclarations.append(namespaceDeclaration);
132 }
133 
134 QXmlStreamNamespaceDeclarations KDSoapValue::namespaceDeclarations() const
135 {
136  return d->m_localNamespaceDeclarations;
137 }
138 
139 void KDSoapValue::setEnvironmentNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &environmentNamespaceDeclarations)
140 {
141  d->m_environmentNamespaceDeclarations = environmentNamespaceDeclarations;
142 }
143 
144 QXmlStreamNamespaceDeclarations KDSoapValue::environmentNamespaceDeclarations() const
145 {
146  return d->m_environmentNamespaceDeclarations;
147 }
148 
150 {
151  // I want to fool the QSharedDataPointer mechanism here...
152  return const_cast<KDSoapValueList &>(d->m_childValues);
153 }
154 
155 bool KDSoapValue::operator==(const KDSoapValue &other) const
156 {
157  return d == other.d;
158 }
159 
160 bool KDSoapValue::operator!=(const KDSoapValue &other) const
161 {
162  return d != other.d;
163 }
164 
165 static QString variantToTextValue(const QVariant &value, const QString &typeNs, const QString &type)
166 {
167  switch (value.userType()) {
168  case QVariant::Char:
169  // fall-through
170  case QVariant::String:
171  return value.toString();
172  case QVariant::Url:
173  // xmlpatterns/data/qatomicvalue.cpp says to do this:
174  return value.toUrl().toString();
175  case QVariant::ByteArray: {
176  const QByteArray data = value.toByteArray();
178  if (type == QLatin1String("hexBinary")) {
179  const QByteArray hb = data.toHex();
180  return QString::fromLatin1(hb.constData(), hb.size());
181  }
182  }
183  // default to base64Binary, like variantToXMLType() does.
184  const QByteArray b64 = value.toByteArray().toBase64();
185  return QString::fromLatin1(b64.constData(), b64.size());
186  }
187  case QVariant::Int:
188  // fall-through
189  case QVariant::LongLong:
190  // fall-through
191  case QVariant::UInt:
192  return QString::number(value.toLongLong());
193  case QVariant::ULongLong:
194  return QString::number(value.toULongLong());
195  case QVariant::Bool:
196  case QMetaType::Float:
197  case QVariant::Double:
198  return value.toString();
199  case QVariant::Time: {
200  const QTime time = value.toTime();
201  if (time.msec()) {
202  // include milli-seconds
203  return time.toString(QLatin1String("hh:mm:ss.zzz"));
204  } else {
205  return time.toString(Qt::ISODate);
206  }
207  }
208  case QVariant::Date:
209  return value.toDate().toString(Qt::ISODate);
210  case QVariant::DateTime: // https://www.w3.org/TR/xmlschema-2/#dateTime
211  return KDDateTime(value.toDateTime()).toDateString();
212  case QVariant::Invalid:
213  qDebug() << "ERROR: Got invalid QVariant in a KDSoapValue";
214  return QString();
215  default:
216  if (value.canConvert<KDDateTime>()) {
217  return value.value<KDDateTime>().toDateString();
218  }
219 
220  if (value.userType() == qMetaTypeId<float>()) {
221  return QString::number(value.value<float>());
222  }
223 
224  qDebug() << QString::fromLatin1("QVariants of type %1 are not supported in "
225  "KDSoap, see the documentation")
226  .arg(QLatin1String(value.typeName()));
227  return value.toString();
228  }
229 }
230 
231 // See also xmlTypeToVariant in serverlib
232 static QString variantToXMLType(const QVariant &value)
233 {
234  switch (value.userType()) {
235  case QVariant::Char:
236  // fall-through
237  case QVariant::String:
238  // fall-through
239  case QVariant::Url:
240  return QLatin1String("xsd:string");
241  case QVariant::ByteArray:
242  return QLatin1String("xsd:base64Binary");
243  case QVariant::Int:
244  // fall-through
245  case QVariant::LongLong:
246  // fall-through
247  case QVariant::UInt:
248  return QLatin1String("xsd:int");
249  case QVariant::ULongLong:
250  return QLatin1String("xsd:unsignedInt");
251  case QVariant::Bool:
252  return QLatin1String("xsd:boolean");
253  case QMetaType::Float:
254  return QLatin1String("xsd:float");
255  case QVariant::Double:
256  return QLatin1String("xsd:double");
257  case QVariant::Time:
258  return QLatin1String("xsd:time"); // correct? xmlpatterns fallsback to datetime because of missing timezone
259  case QVariant::Date:
260  return QLatin1String("xsd:date");
261  case QVariant::DateTime:
262  return QLatin1String("xsd:dateTime");
263  default:
264  if (value.userType() == qMetaTypeId<float>()) {
265  return QLatin1String("xsd:float");
266  }
267  if (value.canConvert<KDDateTime>()) {
268  return QLatin1String("xsd:dateTime");
269  }
270 
271  qDebug() << value;
272 
273  qDebug() << QString::fromLatin1("variantToXmlType: QVariants of type %1 are not supported in "
274  "KDSoap, see the documentation")
275  .arg(QLatin1String(value.typeName()));
276  return QString();
277  }
278 }
279 
280 void KDSoapValue::writeElement(KDSoapNamespacePrefixes &namespacePrefixes, QXmlStreamWriter &writer, KDSoapValue::Use use,
281  const QString &messageNamespace, bool forceQualified) const
282 {
283  Q_ASSERT(!name().isEmpty());
284  if (!d->m_nameNamespace.isEmpty() && d->m_nameNamespace != messageNamespace) {
285  forceQualified = true;
286  }
287 
288  if (d->m_qualified || forceQualified) {
289  const QString ns = d->m_nameNamespace.isEmpty() ? messageNamespace : d->m_nameNamespace;
290 
291  // TODO: if the prefix is new, we want to do namespacePrefixes.insert()
292  // But this means figuring out n2/n3/n4 the same way Qt does...
293 
294  writer.writeStartElement(ns, name());
295  } else {
296  writer.writeStartElement(name());
297  }
298  writeElementContents(namespacePrefixes, writer, use, messageNamespace);
299  writer.writeEndElement();
300 }
301 
302 void KDSoapValue::writeElementContents(KDSoapNamespacePrefixes &namespacePrefixes, QXmlStreamWriter &writer, KDSoapValue::Use use,
303  const QString &messageNamespace) const
304 {
305  const QVariant value = this->value();
306 
307  for (const QXmlStreamNamespaceDeclaration &decl : qAsConst(d->m_localNamespaceDeclarations)) {
308  writer.writeNamespace(decl.namespaceUri().toString(), decl.prefix().toString());
309  }
310 
311  if (isNil() && d->m_nillable) {
312  writer.writeAttribute(KDSoapNamespaceManager::xmlSchemaInstance2001(), QLatin1String("nil"), QLatin1String("true"));
313  }
314 
315  if (use == EncodedUse) {
316  // use=encoded means writing out xsi:type attributes
317  QString type;
318  if (!this->type().isEmpty()) {
319  type = namespacePrefixes.resolve(this->typeNs(), this->type());
320  }
321  if (type.isEmpty() && !value.isNull()) {
322  type = variantToXMLType(value); // fallback
323  }
324  if (!type.isEmpty()) {
325  writer.writeAttribute(KDSoapNamespaceManager::xmlSchemaInstance2001(), QLatin1String("type"), type);
326  }
327 
328  const KDSoapValueList list = this->childValues();
329  const bool isArray = !list.arrayType().isEmpty();
330  if (isArray) {
331  writer.writeAttribute(KDSoapNamespaceManager::soapEncoding(), QLatin1String("arrayType"),
332  namespacePrefixes.resolve(list.arrayTypeNs(), list.arrayType()) + QLatin1Char('[') + QString::number(list.count())
333  + QLatin1Char(']'));
334  }
335  }
336  writeChildren(namespacePrefixes, writer, use, messageNamespace, false);
337 
338  if (!value.isNull()) {
339  const QString txt = variantToTextValue(value, this->typeNs(), this->type());
340  if (!txt.isEmpty()) { // In Qt6, a null string doesn't lead to a null variant anymore
341  writer.writeCharacters(txt);
342  }
343  }
344 }
345 
346 void KDSoapValue::writeChildren(KDSoapNamespacePrefixes &namespacePrefixes, QXmlStreamWriter &writer, KDSoapValue::Use use,
347  const QString &messageNamespace, bool forceQualified) const
348 {
349  const KDSoapValueList &args = childValues();
350  const auto attributes = args.attributes();
351  for (const KDSoapValue &attr : attributes) {
352  // Q_ASSERT(!attr.value().isNull());
353 
354  const QString attributeNamespace = attr.namespaceUri();
355  if (attr.isQualified() || forceQualified) {
356  writer.writeAttribute(attributeNamespace, attr.name(), variantToTextValue(attr.value(), attr.typeNs(), attr.type()));
357  } else {
358  writer.writeAttribute(attr.name(), variantToTextValue(attr.value(), attr.typeNs(), attr.type()));
359  }
360  }
361  KDSoapValueListIterator it(args);
362  while (it.hasNext()) {
363  const KDSoapValue &element = it.next();
364  element.writeElement(namespacePrefixes, writer, use, messageNamespace, forceQualified);
365  }
366 }
367 
369 
370 QDebug operator<<(QDebug dbg, const KDSoapValue &value)
371 {
372  dbg.space() << value.name() << value.value();
373  if (!value.childValues().isEmpty()) {
374  dbg << "<children>";
376  while (it.hasNext()) {
377  const KDSoapValue &child = it.next();
378  dbg << child;
379  }
380  dbg << "</children>";
381  }
382  if (!value.childValues().attributes().isEmpty()) {
383  dbg << "<attributes>";
384  QListIterator<KDSoapValue> it(value.childValues().attributes());
385  while (it.hasNext()) {
386  const KDSoapValue &child = it.next();
387  dbg << child;
388  }
389  dbg << "</attributes>";
390  }
391  return dbg;
392 }
393 
394 void KDSoapValue::setType(const QString &nameSpace, const QString &type)
395 {
396  d->m_typeNamespace = nameSpace;
397  d->m_typeName = type;
398 }
399 
400 QString KDSoapValue::typeNs() const
401 {
402  return d->m_typeNamespace;
403 }
404 
405 QString KDSoapValue::type() const
406 {
407  return d->m_typeName;
408 }
409 
411 {
412  KDSoapValueList valueList;
413 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
414  const QStringList list = value().toString().split(QLatin1Char(' '), Qt::SkipEmptyParts);
415 #else
416  const QStringList list = value().toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
417 #endif
418  valueList.reserve(list.count());
419  for (const QString &part : qAsConst(list)) {
420  KDSoapValue value(*this);
421  value.setValue(part);
422  valueList << value;
423  }
424  return valueList;
425 }
426 
427 KDSoapValue KDSoapValueList::child(const QString &name) const
428 {
429  for (const KDSoapValue &val : qAsConst(*this)) {
430  if (val.name() == name) {
431  return val;
432  }
433  }
434  return KDSoapValue();
435 }
436 
437 void KDSoapValueList::setArrayType(const QString &nameSpace, const QString &type)
438 {
439  m_arrayType = qMakePair(nameSpace, type);
440 }
441 
443 {
444  return m_arrayType.first;
445 }
446 
448 {
449  return m_arrayType.second;
450 }
451 
452 void KDSoapValueList::addArgument(const QString &argumentName, const QVariant &argumentValue, const QString &typeNameSpace, const QString &typeName)
453 {
454  append(KDSoapValue(argumentName, argumentValue, typeNameSpace, typeName));
455 }
456 
458 {
459  return d->m_nameNamespace;
460 }
461 
462 void KDSoapValue::setNamespaceUri(const QString &ns)
463 {
464  d->m_nameNamespace = ns;
465 }
466 
467 QByteArray KDSoapValue::toXml(KDSoapValue::Use use, const QString &messageNamespace) const
468 {
469  QByteArray data;
470  QXmlStreamWriter writer(&data);
471  writer.writeStartDocument();
472 
473  KDSoapNamespacePrefixes namespacePrefixes;
474  namespacePrefixes.writeStandardNamespaces(writer);
475 
476  writeElement(namespacePrefixes, writer, use, messageNamespace, false);
477  writer.writeEndDocument();
478 
479  return data;
480 }
static QString variantToTextValue(const QVariant &value, const QString &typeNs, const QString &type)
static QString variantToXMLType(const QVariant &value)
QDebug operator<<(QDebug dbg, const KDSoapValue &value)
uint qHash(const KDSoapValue &value)
Definition: KDSoapValue.cpp:49
QListIterator< KDSoapValue > KDSoapValueListIterator
Definition: KDSoapValue.h:394
QString toDateString() const
Definition: KDDateTime.cpp:103
static QString xmlSchemaInstance2001()
void writeStandardNamespaces(QXmlStreamWriter &writer, KDSoap::SoapVersion version=KDSoap::SOAP1_1, bool messageAddressingEnabled=false, KDSoapMessageAddressingProperties::KDSoapAddressingNamespace messageAddressingNamespace=KDSoapMessageAddressingProperties::Addressing200508)
QString resolve(const QString &ns, const QString &localName) const
QList< KDSoapValue > & attributes()
Definition: KDSoapValue.h:375
QString arrayTypeNs() const
KDSoapValue child(const QString &name) const
QString arrayType() const
void addArgument(const QString &argumentName, const QVariant &argumentValue, const QString &typeNameSpace=QString(), const QString &typeName=QString())
void setArrayType(const QString &nameSpace, const QString &type)
KDSoapValueList & childValues() const
@ EncodedUse
each message part references an abstract type using the xsi:type attribute
Definition: KDSoapValue.h:268
QXmlStreamNamespaceDeclarations environmentNamespaceDeclarations() const
void addNamespaceDeclaration(const QXmlStreamNamespaceDeclaration &namespaceDeclaration)
QXmlStreamNamespaceDeclarations namespaceDeclarations() const
void setNamespaceUri(const QString &ns)
void setName(const QString &name)
Definition: KDSoapValue.cpp:99
QString namespaceUri() const
void setNillable(bool nillable)
Definition: KDSoapValue.cpp:89
void setType(const QString &nameSpace, const QString &type)
QVariant value() const
void setNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &namespaceDeclarations)
QString typeNs() const
QString name() const
Definition: KDSoapValue.cpp:94
bool operator==(const KDSoapValue &other) const
void setValue(const QVariant &value)
bool isNull() const
Definition: KDSoapValue.cpp:79
QString type() const
void setEnvironmentNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &environmentNamespaceDeclarations)
bool isQualified() const
QByteArray toXml(Use use=LiteralUse, const QString &messageNamespace=QString()) const
bool isNil() const
Definition: KDSoapValue.cpp:84
KDSoapValueList split() const
bool operator!=(const KDSoapValue &other) const
void setQualified(bool qualified)

© 2010-2022 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-soap/
Generated on Tue Jun 13 2023 12:18:34 for KD SOAP API Documentation by doxygen 1.9.1