diff --git a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalProcessor.java b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalProcessor.java index c54b263f718..5b249dd3a28 100644 --- a/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalProcessor.java +++ b/core/rio/jsonld/src/main/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalProcessor.java @@ -10,13 +10,7 @@ *******************************************************************************/ package org.eclipse.rdf4j.rio.jsonld; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; /** @@ -127,7 +121,16 @@ private static Object expandContextInDepth(Object input) { if (graph.containsKey(objectsPredId) && !currentNode.get(ID).equals(objectsPredId) && !currentTreeNode.hasPassedThrough(objectsPredId)) { children.add(objectsPredId); - objectsPredSubjPairs.set(i, (Map) graph.get(objectsPredId)); + Map tuples = (Map) graph.get(objectsPredId); + Map copiedTuples = tuples.entrySet().stream().map(tuple -> { + Map.Entry entry = new AbstractMap.SimpleEntry<>(tuple); + if (tuple.getValue() instanceof List) { + List copy = new ArrayList<>((Collection) tuple.getValue()); + entry.setValue(copy); + } + return entry; + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + objectsPredSubjPairs.set(i, copiedTuples); frontier.add(new TreeNode(objectsPredSubjPairs.get(i), currentTreeNode)); } } diff --git a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalWriterTest.java b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalWriterTest.java index 61bc117e4ba..4eab8062e42 100644 --- a/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalWriterTest.java +++ b/core/rio/jsonld/src/test/java/org/eclipse/rdf4j/rio/jsonld/JSONLDHierarchicalWriterTest.java @@ -299,6 +299,35 @@ public void testOrderDuplicatedChild() throws IOException { verifyOutput(); } + /** + * + * @throws IOException + * @see GH-4833 + */ + @Test + public void testMultipleLoops() throws IOException { + addStatement(vf.createIRI("sch:node1"), vf.createIRI("sch:pred1"), vf.createIRI("sch:node2")); + + addStatement(vf.createIRI("sch:node2"), vf.createIRI("sch:pred2"), vf.createIRI("sch:node3")); + addStatement(vf.createIRI("sch:node2"), vf.createIRI("sch:pred2"), vf.createIRI("sch:node4")); + addStatement(vf.createIRI("sch:node2"), vf.createIRI("sch:pred2"), vf.createIRI("sch:node5")); + + addStatement(vf.createIRI("sch:node2"), vf.createIRI("sch:pred3"), vf.createIRI("sch:node1")); + + addStatement(vf.createIRI("sch:node3"), vf.createIRI("sch:pred4"), vf.createIRI("sch:node2")); + addStatement(vf.createIRI("sch:node3"), vf.createIRI("sch:pred5"), vf.createIRI("sch:node6")); + + addStatement(vf.createIRI("sch:node4"), vf.createIRI("sch:pred4"), vf.createIRI("sch:node2")); + + addStatement(vf.createIRI("sch:node5"), vf.createIRI("sch:pred4"), vf.createIRI("sch:node2")); + addStatement(vf.createIRI("sch:node5"), vf.createIRI("sch:pred5"), vf.createIRI("sch:node6")); + + addStatement(vf.createIRI("sch:node6"), vf.createIRI("sch:pred6"), vf.createIRI("sch:node3")); + + verifyOutput(); + + } + private void addStatement(Resource subject, IRI predicate, Value object, Resource context) { model.add(vf.createStatement(subject, predicate, object, context)); } diff --git a/core/rio/jsonld/src/test/resources/serialized/testMultipleLoops.json b/core/rio/jsonld/src/test/resources/serialized/testMultipleLoops.json new file mode 100644 index 00000000000..324acd774f2 --- /dev/null +++ b/core/rio/jsonld/src/test/resources/serialized/testMultipleLoops.json @@ -0,0 +1,43 @@ +[ { + "@id" : "sch:node2", + "sch:pred2" : [ { + "@id" : "sch:node3", + "sch:pred4" : [ { + "@id" : "sch:node2" + } ], + "sch:pred5" : [ { + "@id" : "sch:node6", + "sch:pred6" : [ { + "@id" : "sch:node3" + } ] + } ] + }, { + "@id" : "sch:node4", + "sch:pred4" : [ { + "@id" : "sch:node2" + } ] + }, { + "@id" : "sch:node5", + "sch:pred4" : [ { + "@id" : "sch:node2" + } ], + "sch:pred5" : [ { + "@id" : "sch:node6", + "sch:pred6" : [ { + "@id" : "sch:node3", + "sch:pred4" : [ { + "@id" : "sch:node2" + } ], + "sch:pred5" : [ { + "@id" : "sch:node6" + } ] + } ] + } ] + } ], + "sch:pred3" : [ { + "@id" : "sch:node1", + "sch:pred1" : [ { + "@id" : "sch:node2" + } ] + } ] +} ] \ No newline at end of file