/// <summary> /// Serializes the given graph to the given stream using Turtle data format. /// </summary> internal static void Serialize(RDFGraph graph, Stream outputStream) { try { #region serialize using (StreamWriter sw = new StreamWriter(outputStream, Encoding.UTF8)) { #region prefixes //Write the namespaces collected by the graph foreach (var ns in RDFModelUtilities.GetGraphNamespaces(graph).OrderBy(n => n.NamespacePrefix)) { sw.WriteLine("@prefix " + ns.NamespacePrefix + ": <" + ns.NamespaceUri + ">."); } sw.WriteLine("@base <" + graph.Context + ">.\n"); #endregion #region linq //Group the graph's triples by subj and pred var groupedList = (from triple in graph orderby triple.Subject.ToString(), triple.Predicate.ToString() group triple by new { subj = triple.Subject.ToString(), pred = triple.Predicate.ToString() }); var groupedListLast = groupedList.Last(); #endregion #region triples String actualSubj = String.Empty; String abbreviatedSubj = String.Empty; String actualPred = String.Empty; String abbreviatedPred = String.Empty; const String spaceConst = " "; StringBuilder result = new StringBuilder(); //Iterate over the calculated groups foreach (var group in groupedList) { var groupLast = group.Last(); #region subj //Reset the flag of subj printing for the new iteration Boolean subjPrint = false; //New subj found: write the finished Turtle token to the file, then start collecting the new one if (!actualSubj.Equals(group.Key.subj, StringComparison.Ordinal)) { if (result.Length > 0) { result.Replace(";", ".", result.Length - 4, 1); sw.Write(result.ToString()); result.Remove(0, result.Length - 1); } actualSubj = group.Key.subj; actualPred = String.Empty; if (!actualSubj.StartsWith("_:")) { abbreviatedSubj = AbbreviateNamespace(actualSubj); } else { abbreviatedSubj = actualSubj; } result.Append(abbreviatedSubj + " "); subjPrint = true; } #endregion #region predObjList //Iterate over the triples of the current group foreach (var triple in group) { #region pred //New pred found: collect it to the actual Turtle token. if (!actualPred.Equals(triple.Predicate.ToString(), StringComparison.Ordinal)) { if (!subjPrint) { result.Append(spaceConst.PadRight(abbreviatedSubj.Length + 1)); //pretty-printing spaces to align the predList } actualPred = triple.Predicate.ToString(); abbreviatedPred = AbbreviateNamespace(actualPred); //Turtle goody for "rdf:type" shortcutting to "a" if (abbreviatedPred == RDFVocabulary.RDF.PREFIX + ":type") { abbreviatedPred = "a"; } result.Append(abbreviatedPred + " "); } #endregion #region object //Collect the object or the literal to the Turtle token if (triple.TripleFlavor == RDFModelEnums.RDFTripleFlavors.SPO) { String obj = triple.Object.ToString(); if (!obj.StartsWith("_:")) { result.Append(AbbreviateNamespace(obj)); } else { result.Append(obj); } } #endregion #region literal else { //Detect presence of long-literals var litValDelim = "\""; if (regexTTL.Match(triple.Object.ToString()).Success) { litValDelim = "\"\"\""; } if (triple.Object is RDFTypedLiteral) { String tLit = litValDelim + ((RDFTypedLiteral)triple.Object).Value.Replace("\\", "\\\\") + litValDelim + "^^" + AbbreviateNamespace(RDFModelUtilities.GetDatatypeFromEnum(((RDFTypedLiteral)triple.Object).Datatype)); result.Append(tLit); } else { String pLit = litValDelim + ((RDFPlainLiteral)triple.Object).Value.Replace("\\", "\\\\") + litValDelim; if (((RDFPlainLiteral)triple.Object).Language != String.Empty) { pLit = pLit + "@" + ((RDFPlainLiteral)triple.Object).Language; } result.Append(pLit); } } #endregion #region continuation goody //Then append the appropriated Turtle continuation goody ("," or ";") if (!triple.Equals(groupLast)) { result.Append(", "); } else { result.AppendLine("; "); } #endregion } #endregion #region last group //This is only for the last group, which is not written into the cycle as the others if (group.Key.Equals(groupedListLast.Key)) { result.Replace(";", ".", result.Length - 4, 1); sw.Write(result.ToString()); } #endregion } #endregion } #endregion } catch (Exception ex) { throw new RDFModelException("Cannot serialize Turtle because: " + ex.Message, ex); } }