Repository: apache/jena (java)

Issue:
RDFXML: can't parse rdf:XMLLiteral when round-trip
### Version

5.1.0

### What happened?

Probably this is duplicate of #2473 
Jena produces RDF/XML, which Jena cannot read itself.

```java
String literal =
        "<ncicp:ComplexDefinition>" +
        "<ncicp:def-definition>XXX" +
        "</ncicp:def-definition>" +
        "<ncicp:def-source>NCI-GLOSS</ncicp:def-source></ncicp:ComplexDefinition>";
var m = ModelFactory.createOntologyModel();
m.createResource().addProperty(RDFS.comment, m.createTypedLiteral(literal, RDF.dtXMLLiteral));
StringWriter sw = new StringWriter();
m.write(sw, "rdf/xml");
System.out.println(sw);
StringReader sr = new StringReader(sw.toString());
ModelFactory.createDefaultModel().read(sr, null, "rdf/xml").write(System.out, "ttl");
```

### Relevant output and stacktrace

```shell
Exception in thread "main" org.apache.jena.riot.RiotException: [line: 7, col: 68] The prefix "ncicp" for element "ncicp:ComplexDefinition" is not bound.
	at org.apache.jena.riot.system.ErrorHandlerFactory$ErrorHandlerStd.fatal(ErrorHandlerFactory.java:155)
	at org.apache.jena.riot.lang.rdfxml.rrx.ParserRDFXML_SAX.fatalError(ParserRDFXML_SAX.java:1727)
	at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:181)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:284)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2726)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:542)
	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:889)
	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:825)
	at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
	at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1224)
	at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:637)
	at org.apache.jena.riot.lang.rdfxml.rrx.ReaderRDFXML_SAX.parse(ReaderRDFXML_SAX.java:83)
	at org.apache.jena.riot.lang.rdfxml.rrx.ReaderRDFXML_SAX.read(ReaderRDFXML_SAX.java:65)
	at org.apache.jena.riot.RDFParser.read(RDFParser.java:456)
	at org.apache.jena.riot.RDFParser.parseNotUri(RDFParser.java:442)
	at org.apache.jena.riot.RDFParser.parse(RDFParser.java:379)
	at org.apache.jena.riot.RDFParserBuilder.parse(RDFParserBuilder.java:547)
	at org.apache.jena.riot.adapters.RDFReaderRIOT.read(RDFReaderRIOT.java:72)
	at org.apache.jena.rdf.model.impl.ModelCom.read(ModelCom.java:205)
```


### Are you interested in making a pull request?

None

Pull request description:
GH-2619: Fix writing invalid rdf:XMLLiterals
GitHub issue resolved #2619

Pull request Description: see #2619.

This was a break at Jena4 -> Jena5.

----

 - [x] Tests are included.
 - [x] Key commit messages start with the issue number (GH-xxxx)

By submitting this pull request, I acknowledge that I am making a contribution to the Apache Software Foundation under the terms and conditions of the [Contributor's Agreement](https://www.apache.org/licenses/contributor-agreements.html).


Patch:
diff --git a/jena-core/src/main/java/org/apache/jena/rdfxml/xmloutput/impl/Unparser.java b/jena-core/src/main/java/org/apache/jena/rdfxml/xmloutput/impl/Unparser.java
index db7e80fca1..cd0dc4954c 100644
--- a/jena-core/src/main/java/org/apache/jena/rdfxml/xmloutput/impl/Unparser.java
+++ b/jena-core/src/main/java/org/apache/jena/rdfxml/xmloutput/impl/Unparser.java
@@ -428,13 +428,17 @@ class Unparser {
      * propName '>'
      */
     private boolean wPropertyEltLiteral(WType wt, Property prop, Statement statement, RDFNode rdfNode) {
+        // Is the rule blocked?
         if (prettyWriter.sParseTypeLiteralPropertyElt)
             return false;
         if ( ! (rdfNode instanceof Literal lit) )
             return false;
-
         if (! XMLLiteralType.isXMLLiteral(lit.getDatatype()) )
             return false;
+        // Must be a legal XML fragment.
+        // It must be valid XML and also self-contained XML content (e.g. includes namespaces - they are not inherited from the model).
+        if ( ! lit.asNode().getLiteral().isWellFormed() )
+            return false;
 
         // print out.
         done(statement);


Test patch:
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java
index 7cab9dc100..a65492e2d8 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TS_RiotWriter.java
@@ -24,14 +24,15 @@ import org.junit.runners.Suite.SuiteClasses ;
 
 @RunWith(Suite.class)
 @SuiteClasses(
-{ 
+{
     TestWriterRegistration.class
     , TestRiotWriterGraph.class
     , TestRiotWriterDataset.class
     , TestRDFJSON.class
     , TestTurtleWriter.class
     , TestTriXWriter.class
-    , TestRDFXML_WriterProperties.class
+    , TestWriterRDFXML.class
+    , TestWriterRDFXML_Properties.class
 })
 
 public class TS_RiotWriter
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestWriterRDFXML.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestWriterRDFXML.java
new file mode 100644
index 0000000000..d416562ef6
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestWriterRDFXML.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,rdf:parseType="Literal"
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.riot.writer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.apache.jena.atlas.logging.LogCtl;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.riot.*;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.RDFS;
+import org.junit.Test;
+
+/**
+ * Additional tests for RDF/XML writing.
+ */
+public class TestWriterRDFXML {
+    @Test public void testRDFXMLLIterals_1() {
+        // Valid RDF XMLLiteral.
+        testXMLLiteral("<ns:text xmlns:ns='http://example/ns#'>ABC</ns:text>", true);
+    }
+
+    @Test public void testRDFXMLLIterals_2() {
+        // Valid RDF XMLLiteral. Document fragment.
+        testXMLLiteral("<ns:text xmlns:ns='http://example/ns#'>ABC</ns:text><content>XYZ</content>", true);
+    }
+
+    @Test public void testRDFXMLLIterals_bad_1() {
+        // Invalid RDF XMLLiteral. Not self contained XML - no namespaces
+        testXMLLiteral("<ns:text>ABC</ns:text>", false);
+    }
+
+    @Test public void testRDFXMLLIterals_bad_2() {
+        // Invalid RDF XMLLiteral. Not valid XML - no namespaces
+        testXMLLiteral("<ns:text>ABC", false);
+    }
+
+    private static void testXMLLiteral(String lex, boolean expectValid) {
+        var m = ModelFactory.createDefaultModel();//createOntologyModel();
+        m.createResource().addProperty(RDFS.comment, m.createTypedLiteral(lex, RDF.dtXMLLiteral));
+        String s = RDFWriter.source(m).format(RDFFormat.RDFXML_ABBREV).toString();
+
+        // Valid RDF XML Literal - write as rdf:parseType Literal
+        // Invalid RDF XML Literal - write as rdf:datatype
+
+        assertEquals(expectValid, s.contains("rdf:parseType=\"Literal\""));
+        assertEquals(expectValid, ! s.contains("rdf:datatype="));
+
+        LogCtl.withLevel(SysRIOT.getLogger(), "ERROR", ()->{
+            Graph g = RDFParser.fromString(s, Lang.RDFXML).toGraph();
+        });
+    }
+}
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestRDFXML_WriterProperties.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestWriterRDFXML_Properties.java
similarity index 98%
rename from jena-arq/src/test/java/org/apache/jena/riot/writer/TestRDFXML_WriterProperties.java
rename to jena-arq/src/test/java/org/apache/jena/riot/writer/TestWriterRDFXML_Properties.java
index b9ce3c336a..124487f28a 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestRDFXML_WriterProperties.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestWriterRDFXML_Properties.java
@@ -31,7 +31,7 @@ import org.junit.Assert ;
 import org.junit.Test ;
 
 /** Tests of the RDF/XML writers used via RIOT */
-public class TestRDFXML_WriterProperties {
+public class TestWriterRDFXML_Properties {
 
     static Model model = ModelFactory.createDefaultModel() ;
     static {