Exemple #1
0
bool JsonLd::addContext(
   DynamicObject& context, DynamicObject& in, DynamicObject& out)
{
   bool rval = true;

   // "a" is automatically shorthand for rdf type
   DynamicObject ctx = (context.isNull() ? DynamicObject() : context.clone());
   ctx["a"] = RDF_TYPE;

   // TODO: should context simplification be an option? (ie: remove context
   // entries that are not used in the output)

   // setup output context
   DynamicObject& contextOut = out["#"];
   contextOut->setType(Map);

   // apply context
   _applyContext(ctx, contextOut, NULL, in, out);

   // clean up
   if(contextOut->length() == 0)
   {
      out->removeMember("#");
   }

   return rval;
}
Exemple #2
0
bool JsonLd::normalize(DynamicObject& in, DynamicObject& out)
{
   bool rval = true;

   // initialize output
   out->setType(Map);
   out->clear();

   // create map to store subjects
   DynamicObject subjects;
   subjects->setType(Map);

   // initialize context
   DynamicObject ctx(NULL);
   if(in->hasMember("#"))
   {
      ctx = in["#"];
   }

   // put all subjects in an array for single code path
   DynamicObject input;
   input->setType(Array);
   if(in->hasMember("@") && in["@"]->getType() == Array)
   {
      input.merge(in["@"], true);
   }
   else
   {
      input->append(in);
   }

   // do normalization
   int bnodeId = 0;
   DynamicObjectIterator i = input.getIterator();
   while(rval && i->hasNext())
   {
      rval = _normalize(ctx, i->next(), &subjects, NULL, bnodeId);
   }

   // build output
   if(rval)
   {
      // single subject
      if(subjects->length() == 1)
      {
         DynamicObject subject = subjects.first();
         out.merge(subject, false);

         // FIXME: will need to check predicates for blank nodes as well...
         // and fail if they aren't embeds?

         // strip blank node '@'
         if(_isBlankNode(out))
         {
            out->removeMember("@");
         }
      }
      // multiple subjects
      else
      {
         DynamicObject& array = out["@"];
         array->setType(Array);
         i = subjects.getIterator();
         while(i->hasNext())
         {
            DynamicObject& next = i->next();

            // FIXME: will need to check predicates for blank nodes as well...
            // and fail if they aren't embeds?

            // strip blank node '@'
            if(_isBlankNode(next))
            {
               next->removeMember("@");
            }
            array->append(next);
         }
      }
   }

   return rval;
}
Exemple #3
0
/**
 * Recursively normalizes the given input object.
 *
 * Input: A subject with predicates and possibly embedded other subjects.
 * Output: Either a map of normalized subjects OR a tree of normalized subjects.
 *
 * Normalization Algorithm:
 *
 * Replace the existing context if the input has '#'.
 *
 * For each key-value:
 * 1. Split the key on a colon and look for prefix in the context. If found,
 *    expand the key to an IRI, else it is already an IRI, add <>, save the
 *    new predicate to add to the output.
 * 2. If value is a Map, then it is a subject, set the predicate to the
 *    subject's '@' IRI value and recurse into it. Else goto #3.
 * 3. Look up the key in the context to find type coercion info. If not found,
 *    goto #4, else goto #5.
 * 4. Check the value for an integer, double, or boolean. If matched, set
 *    type according to xsd types. If not matched, look for <>, if found,
 *    do CURIE vs. IRI check like #1 and create appropriate value.
 * 5. If type coercion entry is a string, encode the value using the specific
 *    type. If it is an array, check the type in order of preference. If an
 *    unrecognized type (non-xsd, non-IRI) is provided, throw an exception.
 *
 * @param ctx the context to use (changes during recursion as necessary).
 * @param in the input object.
 * @param subjects a map of normalized subjects.
 * @param out a tree normalized objects.
 * @param bnodeId the last blank node ID used.
 *
 * @return true on success, false on failure with exception set.
 */
static bool _normalize(
   DynamicObject ctx, DynamicObject& in,
   DynamicObject* subjects, DynamicObject* out, int& bnodeId)
{
   bool rval = true;

   // FIXME: validate context (check for non-xsd types in type coercion arrays)

   if(!in.isNull())
   {
      // update context
      if(in->hasMember("#"))
      {
         ctx = in["#"];
      }

      // vars for normalization state
      DynamicObject subject(NULL);
      if(subjects != NULL)
      {
         subject = DynamicObject();
         subject->setType(Map);

         // assign blank node ID as needed
         if(!in->hasMember("@"))
         {
            string bnodeKey = _createBlankNodeId(++bnodeId);
            subject["@"] = bnodeKey.c_str();
         }
      }
      string nKey;

      // iterate over key-values
      DynamicObjectIterator i = in.getIterator();
      while(rval && i->hasNext())
      {
         DynamicObject& value = i->next();
         const char* key = i->getName();

         // skip context keys
         if(key[0] == '#')
         {
            continue;
         }

         // get normalized key
         nKey = _normalizeValue(ctx, key, RDF_TYPE_IRI, NULL, NULL);

         // put values in an array for single code path
         DynamicObject values;
         values->setType(Array);
         if(value->getType() == Array)
         {
            values.merge(value, true);

            // preserve array structure when not using subjects map
            if(out != NULL)
            {
               (*out)[nKey.c_str()]->setType(Array);
            }
         }
         else
         {
            values->append(value);
         }

         // normalize all values
         DynamicObjectIterator vi = values.getIterator();
         while(rval && vi->hasNext())
         {
            DynamicObject v = vi->next();
            if(v->getType() == Map)
            {
               if(subjects != NULL)
               {
                  // get a normalized subject for the value
                  string vSubject;
                  if(v->hasMember("@"))
                  {
                     // normalize existing subject
                     vSubject = _normalizeValue(
                        ctx, v["@"], RDF_TYPE_IRI, NULL, NULL);
                  }
                  else
                  {
                     // generate the next blank node ID in order to preserve
                     // the blank node embed in the code below
                     vSubject = _createBlankNodeId(bnodeId + 1);
                  }

                  // determine if value is a blank node
                  bool isBNode = _isBlankNode(v);

                  // update non-blank node subject (use value's subject IRI)
                  if(!isBNode)
                  {
                     _setPredicate(subject, nKey.c_str(), vSubject.c_str());
                  }

                  // recurse
                  rval = _normalize(ctx, v, subjects, out, bnodeId);

                  // preserve embedded blank node
                  if(rval && isBNode)
                  {
                     // remove embed from top-level subjects
                     DynamicObject embed = (*subjects)[vSubject.c_str()];
                     (*subjects)->removeMember(vSubject.c_str());
                     embed->removeMember("@");
                     _setEmbed(subject, nKey.c_str(), embed);
                  }
               }
               else
               {
                  // update out and recurse
                  DynamicObject next = (*out)[nKey.c_str()];
                  if(value->getType() == Array)
                  {
                     next = next->append();
                  }
                  else
                  {
                     next->setType(Map);
                  }
                  rval = _normalize(ctx, v, subjects, &next, bnodeId);
               }
            }
            else
            {
               _setPredicate(
                  (subjects != NULL) ? subject : *out, nKey.c_str(),
                  _normalizeValue(ctx, v, RDF_TYPE_UNKNOWN, key, NULL).c_str());
            }
         }
      }

      // add subject to map
      if(subjects != NULL)
      {
         (*subjects)[subject["@"]->getString()] = subject;
      }
   }

   return rval;
}