Skip to content

RDF lists created using RDFCollections.asRDF() are not properly serialized using TurtleWriter #3681

@jetztgradnet

Description

@jetztgradnet

Current Behavior

When serializing an RDF list created using RDFCollections.asRDF() with TurtleWriter the first segment of the list is not properly inlined as it is not recognized as a well-formed list.

The result looks as this:

@prefix ex: <http://example.com/ns#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

ex:Cities ex:list [ a rdf:List;
      rdf:first ex:NewYork;
      rdf:rest (ex:Rio ex:Tokyo)
    ] .

Please notice the first part with explicit rdf:first and rdf:rest statements instead of the inlining of the list, which is done for the last two segments of the list.

This is caused by org.eclipse.rdf4j.rio.turtle.TurtleWriter.isWellFormedCollection() not recognizing the rdf:type rdf:List type statement for the first part of the list segment which is generated in RDFCollections.asRDF() (or more specifically in org.eclipse.rdf4j.model.util.RDFCollections.consumeCollection(Iterable<?>, Resource, Consumer<Statement>, ValueFactory, Resource...)).
A simple solution would be to adjust org.eclipse.rdf4j.rio.turtle.TurtleWriter.isWellFormedCollection() to recognize and accept that statement when checking for additional statements within the list.

Expected Behavior

The proper visualization of the list would instead look like this:

@prefix ex: <http://example.com/ns#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

ex:Cities ex:list (ex:NewYork ex:Rio ex:Tokyo) .

Steps To Reproduce

This test case reproduces the behavior:

package com.metaphacts.services.ontologies;

import static org.junit.Assert.assertEquals;

import java.io.StringWriter;
import java.util.List;

import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.impl.TreeModel;
import org.eclipse.rdf4j.model.util.RDFCollections;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;
import org.eclipse.rdf4j.rio.WriterConfig;
import org.eclipse.rdf4j.rio.helpers.BasicWriterSettings;
import org.junit.Test;

public class RDFCollectionsTest {
    @Test
    public void testBNodeValuesInList() throws Exception {
        final String ns = "http://example.com/ns#";
        IRI newyork = Values.iri(ns, "NewYork");
        IRI rio = Values.iri(ns, "Rio");
        IRI tokyo = Values.iri(ns, "Tokyo");
        IRI cities = Values.iri(ns, "Cities");
        IRI exList = Values.iri(ns, "list");
        BNode listHead = Values.bnode("n1");
        
        Model data = new TreeModel();
        data.setNamespace("ex", ns);
        data.setNamespace("rdf", RDF.NAMESPACE);
        RDFCollections.asRDF(List.of(newyork, rio, tokyo), listHead, data);
        data.add(cities, exList, listHead);

        String expected = "" +
                "@prefix ex: <http://example.com/ns#> .\n" +
                "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n" +
                "\n" +
                "ex:Cities ex:list (ex:NewYork ex:Rio ex:Tokyo) .\n";

        StringWriter stringWriter = new StringWriter();
        WriterConfig config = new WriterConfig();
        config.set(BasicWriterSettings.INLINE_BLANK_NODES, true);
        config.set(BasicWriterSettings.PRETTY_PRINT, true);
        Rio.write(data, stringWriter, RDFFormat.TURTLE, config);
        String actual = stringWriter.toString();
        
//        System.out.println("### ACTUAL ###");
//        System.out.println(actual);
//        System.out.println("#################\n");
        
//        System.out.println("### EXPECTED ###");
//        System.out.println(expected);
//        System.out.println("#################\n");

        assertEquals("The visual representation should be a proper list for all elements", expected, actual);
    }
}

Adding this line before serializing the model as string makes the test work, but the fix should not remove that statement but rather recognize and accept it.

        // deleting the type statement in the list would make it work
        // [ a rdf:List ]
        // data.remove(listHead, RDF.TYPE, RDF.LIST);

Version

3.7.4 (and likely earlier versions as well)

Are you interested in contributing a solution yourself?

Perhaps?

Anything else?

No response

Metadata

Metadata

Assignees

Labels

🐞 bugissue is a bug📦 rioaffects the Rio RDF Parser/Writer toolkit

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions