You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@daffodil.apache.org by "Thompson, Dave" <dt...@owlcyberdefense.com> on 2021/04/29 16:03:52 UTC

RE: [daffodil] branch master updated: Feature Aware DaffodilParseXMLReader and Prefixes Fixes

Lola,

You updated DAFFODIL-2422 and DAFFODIL-2457 To resolved but didn't update the Fix Version.

Could you please update  the Fix Versions for those tickets to 3.1.0.

Thanks,

Dave


-----Original Message-----
From: olabusayo@apache.org 
Sent: Wednesday, April 28, 2021 1:50 PM
To: commits@daffodil.apache.org
Subject: [daffodil] branch master updated: Feature Aware DaffodilParseXMLReader and Prefixes Fixes

This is an automated email from the ASF dual-hosted git repository.

olabusayo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new fce9316  Feature Aware DaffodilParseXMLReader and Prefixes Fixes
fce9316 is described below

commit fce93164c2dd453da2d10dc9b3f684d61244c0b6
Author: olabusayoT <50...@users.noreply.github.com>
AuthorDate: Fri Jan 8 16:00:46 2021 -0500

    Feature Aware DaffodilParseXMLReader and Prefixes Fixes
    
    - Makes XMLReader and OutputStream content handler aware of/handle appropriately the namespaces and namespace-prefixes features
    - More uniform handling of Attributes object in relation to attributes and prefixMappings
    - forces prefixMapping to be in the same order as the input, otherwise it gets reversed, affecting the output of getPrefix
    - adds functions to do getURI and getPrefix without nullpointerexceptions
    - adds prefix function in ERD to do lookup of namedQName.namespace in erd.minimizedScope
    - Replaces instances of prefixOrNull with a lookup using the current namespace, as well as the namespacebinding (either MinimizedScope or ElementBase.namespaces)
    - Removes prefixOrNull
    - Fixes XMLDifferenceException to include a non-stripped version of expected, if checkNamespaces/checkPrefixes is true
    - updates feature checks to be before every parse and made them read-only during a parse
    - adds scaladoc to test methods and updated API documentation
    - updates error message for features
    - changes != to ne for namespaceBindings
    - refactors SAX module tests to move common functionality into Utils class
    - adds tests for false/false, false/true, true/true, and true/false (default)
    - adds xsi:nil namespace tests
    - adds trace tests for DaffodilParseXMLReader to ensure it was correctly generating the expected events depending on the features sent it
    
    DAFFODIL-2422 DAFFODIL-2457
---
 .../org/apache/daffodil/dsom/ElementBase.scala     |   2 +-
 ...xample_a02_targetnamespace_unqualified.dfdl.xsd |  48 ++
 .../example_c02_targetnamespace_qualified.dfdl.xsd |  57 +++
 .../example_nested_namespaces_qualified.dfdl.xsd   |  71 +++
 ...sted_namespaces_qualified_with_default.dfdl.xsd |  69 +++
 .../example_nested_namespaces_unqualified.dfdl.xsd |  67 +++
 .../test/example_no_targetnamespace.dfdl.xsd       |  54 +++
 .../daffodil/processor/TestSAXParseAPI.scala       | 515 +++++++++++++++++++--
 .../processor/TestSAXParseUnparseAPI.scala         | 103 ++---
 .../daffodil/processor/TestSAXUnparseAPI.scala     |  14 +-
 .../apache/daffodil/processor/TestSAXUtils.scala   | 356 ++++++++++++++
 .../org/apache/daffodil/japi/package-info.java     |   5 +
 .../org/apache/daffodil/example/TestJavaAPI.java   |   3 +
 .../scala/org/apache/daffodil/xml/QNameBase.scala  |   1 -
 .../scala/org/apache/daffodil/xml/XMLUtils.scala   |  63 ++-
 .../daffodil/infoset/JDOMInfosetOutputter.scala    |   2 +-
 .../daffodil/infoset/SAXInfosetOutputter.scala     | 117 +++--
 .../infoset/ScalaXMLInfosetOutputter.scala         |   4 +-
 .../daffodil/infoset/XMLTextInfosetOutputter.scala |   2 +-
 .../DaffodilParseOutputStreamContentHandler.scala  | 177 ++++++-
 .../processors/DaffodilParseXMLReader.scala        |  83 ++--
 .../apache/daffodil/processors/RuntimeData.scala   |   6 +-
 .../scala/org/apache/daffodil/sapi/package.scala   |   5 +
 .../org/apache/daffodil/example/TestScalaAPI.scala |   3 +
 .../daffodil/tdml/TDMLInfosetOutputter.scala       |   2 +
 .../tdml/processor/DaffodilTDMLDFDLProcessor.scala |  12 +-
 26 files changed, 1629 insertions(+), 212 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
index 90d4c29..3bc515e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
@@ -203,7 +203,7 @@ trait ElementBase
     val childrenRequiredNSBindings =
       this.elementChildren.flatMap { _.thisElementsRequiredNamespaceBindings }.toSet
 
-    val myRequiredNSBinding = Set((namedQName.prefixOrNull, namedQName.namespace))
+    val myRequiredNSBinding = Set((this.namespaces.getPrefix(namedQName.namespace), namedQName.namespace))
     val nilNSBinding = {
       if (!isNillable) Set()
       else {
diff --git a/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd b/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
new file mode 100644
index 0000000..1e6b980
--- /dev/null
+++ b/daffodil-core/src/test/resources/test/example_a02_targetnamespace_unqualified.dfdl.xsd
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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,
+  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.
+-->
+
+<xs:schema targetNamespace="http://a02.com"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/">
+
+  <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format ref="GeneralFormat"
+        separator=""
+        initiator=""
+        separatorPosition="infix"
+        ignoreCase="no"
+        separatorSuppressionPolicy="anyEmpty"
+        terminator=""
+        occursCountKind="parsed"
+        initiatedContent="no"
+        representation="text"
+        textNumberRep="standard"
+        encoding="ASCII"
+        textTrimKind="none"
+        leadingSkip='0'/>
+    </xs:appinfo>
+  </xs:annotation>
+
+  <xs:element name="inty" type="xs:int" dfdl:lengthKind="delimited"/>
+  <xs:element name="intx" type="xs:int" nillable="true" dfdl:nilKind="literalValue" dfdl:nilValue="^"/>
+
+
+</xs:schema>
diff --git a/daffodil-core/src/test/resources/test/example_c02_targetnamespace_qualified.dfdl.xsd b/daffodil-core/src/test/resources/test/example_c02_targetnamespace_qualified.dfdl.xsd
new file mode 100644
index 0000000..b3de514
--- /dev/null
+++ b/daffodil-core/src/test/resources/test/example_c02_targetnamespace_qualified.dfdl.xsd
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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,
+  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.
+-->
+
+<xs:schema targetNamespace="http://c02.com"
+  xmlns="http://c02.com"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+  elementFormDefault="qualified">
+
+  <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format ref="GeneralFormat"
+        separator=""
+        initiator=""
+        separatorPosition="infix"
+        ignoreCase="no"
+        separatorSuppressionPolicy="anyEmpty"
+        terminator=""
+        occursCountKind="parsed"
+        initiatedContent="no"
+        representation="text"
+        textNumberRep="standard"
+        encoding="ASCII"
+        textTrimKind="none"
+        leadingSkip='0'/>
+    </xs:appinfo>
+  </xs:annotation>
+
+   <xs:element name="c">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element name="d" type="xs:string" dfdl:lengthKind="pattern" dfdl:lengthPattern="[A-Za-z^]{1,5}"
+          nillable="true" dfdl:nilKind="literalValue" dfdl:nilValue="^"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+
+
+</xs:schema>
diff --git a/daffodil-core/src/test/resources/test/example_nested_namespaces_qualified.dfdl.xsd b/daffodil-core/src/test/resources/test/example_nested_namespaces_qualified.dfdl.xsd
new file mode 100644
index 0000000..9ea4952
--- /dev/null
+++ b/daffodil-core/src/test/resources/test/example_nested_namespaces_qualified.dfdl.xsd
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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,
+  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.
+-->
+
+<xs:schema targetNamespace="http://b02.com"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+  xmlns:a02="http://a02.com"
+  xmlns:b02="http://b02.com"
+  elementFormDefault="qualified">
+
+  <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format ref="b02:GeneralFormat"
+        separator="."
+        lengthKind="delimited"
+        ignoreCase="no"
+        separatorPosition="infix"
+        separatorSuppressionPolicy="anyEmpty"
+        leadingSkip='0'
+        initiator=""
+        terminator=""
+        occursCountKind="parsed"
+        initiatedContent="no"
+        representation="text"
+        textNumberRep="standard"
+        encoding="ASCII"
+        textTrimKind="none"
+        alignment="implicit"
+        alignmentUnits="bytes"
+        trailingSkip="0"/>
+    </xs:appinfo>
+  </xs:annotation>
+
+  <xs:include schemaLocation="/test/example_no_targetnamespace.dfdl.xsd"/>
+  <xs:import namespace="http://a02.com" schemaLocation="/test/example_a02_targetnamespace_unqualified.dfdl.xsd"/>
+
+  <xs:element name="seq">
+    <xs:complexType>
+      <xs:sequence dfdl:separator=".">
+      <xs:element name="seq2" maxOccurs="unbounded">
+        <xs:complexType>
+            <xs:choice dfdl:choiceLengthKind="implicit">
+              <xs:element ref="a02:inty" dfdl:initiator="-" />
+              <xs:element ref="b02:inty" dfdl:initiator="*"/>
+              <xs:element ref="a02:intx"/>
+            </xs:choice>
+        </xs:complexType>
+      </xs:element>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+
+</xs:schema>
diff --git a/daffodil-core/src/test/resources/test/example_nested_namespaces_qualified_with_default.dfdl.xsd b/daffodil-core/src/test/resources/test/example_nested_namespaces_qualified_with_default.dfdl.xsd
new file mode 100644
index 0000000..8829d98
--- /dev/null
+++ b/daffodil-core/src/test/resources/test/example_nested_namespaces_qualified_with_default.dfdl.xsd
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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,
+  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.
+-->
+
+<xs:schema targetNamespace="http://b02.com"
+  xmlns="http://b02.com"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+  xmlns:c02="http://c02.com"
+  xmlns:b02="http://b02.com"
+  elementFormDefault="qualified">
+
+  <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format ref="b02:GeneralFormat"
+        separator="."
+        lengthKind="delimited"
+        ignoreCase="no"
+        separatorPosition="infix"
+        separatorSuppressionPolicy="anyEmpty"
+        leadingSkip='0'
+        initiator=""
+        terminator=""
+        occursCountKind="parsed"
+        initiatedContent="no"
+        representation="text"
+        textNumberRep="standard"
+        encoding="ASCII"
+        textTrimKind="none"
+        alignment="implicit"
+        alignmentUnits="bytes"
+        trailingSkip="0"/>
+    </xs:appinfo>
+  </xs:annotation>
+
+  <xs:import namespace="http://c02.com" schemaLocation="/test/example_c02_targetnamespace_qualified.dfdl.xsd"/>
+
+  <xs:element name="a">
+    <xs:complexType>
+      <xs:sequence dfdl:separator=".">
+      <xs:element name="b" maxOccurs="unbounded">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element ref="c02:c"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+
+</xs:schema>
diff --git a/daffodil-core/src/test/resources/test/example_nested_namespaces_unqualified.dfdl.xsd b/daffodil-core/src/test/resources/test/example_nested_namespaces_unqualified.dfdl.xsd
new file mode 100644
index 0000000..2595695
--- /dev/null
+++ b/daffodil-core/src/test/resources/test/example_nested_namespaces_unqualified.dfdl.xsd
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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,
+  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.
+-->
+
+<xs:schema targetNamespace="http://b02.com"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+  xmlns:c02="http://c02.com"
+  xmlns:b02="http://b02.com">
+
+  <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format ref="b02:GeneralFormat"
+        separator="."
+        lengthKind="delimited"
+        ignoreCase="no"
+        separatorPosition="infix"
+        separatorSuppressionPolicy="anyEmpty"
+        leadingSkip='0'
+        initiator=""
+        terminator=""
+        occursCountKind="parsed"
+        initiatedContent="no"
+        representation="text"
+        textNumberRep="standard"
+        encoding="ASCII"
+        textTrimKind="none"
+        alignment="implicit"
+        alignmentUnits="bytes"
+        trailingSkip="0"/>
+    </xs:appinfo>
+  </xs:annotation>
+
+  <xs:import namespace="http://c02.com" schemaLocation="/test/example_c02_targetnamespace_qualified.dfdl.xsd"/>
+
+  <xs:element name="a">
+    <xs:complexType>
+      <xs:sequence dfdl:separator=".">
+      <xs:element name="b" maxOccurs="unbounded">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element ref="c02:c"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+
+</xs:schema>
diff --git a/daffodil-core/src/test/resources/test/example_no_targetnamespace.dfdl.xsd b/daffodil-core/src/test/resources/test/example_no_targetnamespace.dfdl.xsd
new file mode 100644
index 0000000..d43916d
--- /dev/null
+++ b/daffodil-core/src/test/resources/test/example_no_targetnamespace.dfdl.xsd
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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,
+  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.
+-->
+<!-- copied from multi_C_02.dfdl.xsd since daffodil-test is not a test dependency,
+      but daffodil-core is a dependency for daffodil-test; as this is just 1 file,
+      we decided to copy it instead of trying to go the dependency route-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/">
+
+  <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format ref="GeneralFormat"
+        separator=""
+        initiator=""
+        separatorPosition="infix"
+        ignoreCase="no"
+        separatorSuppressionPolicy="anyEmpty"
+        terminator=""
+        occursCountKind="parsed"
+        initiatedContent="no"
+        representation="text"
+        textNumberRep="standard"
+        encoding="ASCII"
+        textTrimKind="none"
+        leadingSkip='0'/>
+    </xs:appinfo>
+  </xs:annotation>
+
+  <xs:element name="x">
+    <xs:complexType>
+      <xs:sequence dfdl:separator=".">
+        <xs:element name="y" type="xs:string" dfdl:lengthKind="pattern" dfdl:lengthPattern="[A-Za-z^]{1,5}"
+          nillable="true" dfdl:nilKind="literalValue" dfdl:nilValue="^" maxOccurs="unbounded"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="inty" type="xs:int" dfdl:lengthKind="delimited"/>
+
+</xs:schema>
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseAPI.scala b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseAPI.scala
index 2e15aa0..a3aba83 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseAPI.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseAPI.scala
@@ -24,6 +24,7 @@ import java.io.IOException
 import scala.xml.SAXParseException
 
 import org.apache.daffodil.Implicits.intercept
+import org.apache.daffodil.api.DFDL
 import org.apache.daffodil.processors.DaffodilParseOutputStreamContentHandler
 import org.apache.daffodil.processors.ParseResult
 import org.apache.daffodil.xml.XMLUtils
@@ -40,9 +41,12 @@ import org.xml.sax.SAXNotRecognizedException
 import org.xml.sax.SAXNotSupportedException
 
 class TestSAXParseAPI {
-  import TestSAXParseUnparseAPI._
+  import TestSAXUtils._
 
-  @Test def testDaffodilParseXMLReader_setFeatureUnsupported(): Unit = {
+  /**
+   * tests the case where we attempt to set an unsupported feature
+   */
+  @Test def testDaffodilParseXMLReader_setFeature_unsupported(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val snr = intercept[SAXNotRecognizedException] {
       xmlReader.setFeature("http://xml.org/sax/features/validation", true)
@@ -51,9 +55,24 @@ class TestSAXParseAPI {
     assertTrue(snr.getMessage.contains("Supported features are:"))
   }
 
+  /**
+   * tests the case where we attempt to get an unsupported feature
+   */
+  @Test def testDaffodilParseXMLReader_getFeature_unsupported(): Unit = {
+    val xmlReader = dp.newXMLReaderInstance
+    val snr = intercept[SAXNotRecognizedException] {
+      xmlReader.getFeature("http://xml.org/sax/features/validation")
+    }
+    assertTrue(snr.getMessage.contains("Feature unsupported"))
+    assertTrue(snr.getMessage.contains("Supported features are:"))
+  }
+
+  /**
+   * tests that we can set and get a supported feature
+   */
   @Test def testDaffodilParseXMLReader_get_setFeature(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
-    val feature = "http://xml.org/sax/features/namespace-prefixes"
+    val feature = XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE
     val origValue = xmlReader.getFeature(feature)
     assertFalse(origValue)
     xmlReader.setFeature(feature, true)
@@ -61,6 +80,27 @@ class TestSAXParseAPI {
     assertTrue(newValue)
   }
 
+  /*
+   * tests the case where both supported features are set to false, as it is an illegal state
+   */
+  @Test def testDaffodilParseXMLReader_supportedFeaturesNotFalse(): Unit = {
+    val xmlReader = dp.newXMLReaderInstance
+    val baos = new ByteArrayOutputStream()
+    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baos)
+    xmlReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, false)
+    xmlReader.setFeature(XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE, false)
+    xmlReader.setContentHandler(parseOutputStreamContentHandler)
+    val inArray = testData.getBytes()
+    val snr = intercept[org.xml.sax.SAXException] {
+      xmlReader.parse(inArray)
+    }
+    assertTrue(snr.getMessage.contains("Namespaces and NamespacePrefixes"))
+    assertTrue(snr.getMessage.contains("cannot both be false"))
+  }
+
+  /**
+   * tests the case where we attempt to set an unsupported property
+   */
   @Test def testDaffodilParseXMLReader_setProperty_unsupported(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val property: AnyRef = "Hello"
@@ -70,6 +110,20 @@ class TestSAXParseAPI {
     assertTrue(snr.getMessage.contains("Property unsupported"))
   }
 
+  /**
+   * tests the case where we attempt to get an unsupported property
+   */
+  @Test def testDaffodilParseXMLReader_getProperty_unsupported(): Unit = {
+    val xmlReader = dp.newXMLReaderInstance
+    val snr = intercept[SAXNotRecognizedException] {
+      xmlReader.getProperty("http://xml.org/sax/properties/xml-string")
+    }
+    assertTrue(snr.getMessage.contains("Property unsupported"))
+  }
+
+  /**
+   * tests the case where we attempt to set an invalid value for a supported property
+   */
   @Test def testDaffodilParseXMLReader_setProperty_badValue(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val property: String = XMLUtils.DAFFODIL_SAX_URN_BLOBDIRECTORY
@@ -80,6 +134,9 @@ class TestSAXParseAPI {
     assertTrue(sns.getMessage.contains("Unsupported value for property"))
   }
 
+  /**
+   * tests that we can set and get a supported property
+   */
   @Test def testDaffodilParseXMLReader_get_setProperty(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val property: String = XMLUtils.DAFFODIL_SAX_URN_BLOBPREFIX
@@ -91,6 +148,9 @@ class TestSAXParseAPI {
     assertEquals(propertyVal, newValue.asInstanceOf[String])
   }
 
+  /**
+   * test that we can get and set a contentHandler
+   */
   @Test def testDaffodilParseXMLReader_get_setContentHandler(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val parseContentHandler = new SAXHandler()
@@ -101,6 +161,9 @@ class TestSAXParseAPI {
     assertTrue(newValue.isInstanceOf[SAXHandler])
   }
 
+  /**
+   * test that we can get and set a errorHandler
+   */
   @Test def testDaffodilParseXMLReader_get_setErrorHandler(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val eh = new BuilderErrorHandler()
@@ -111,6 +174,9 @@ class TestSAXParseAPI {
     assertTrue(newValue.isInstanceOf[BuilderErrorHandler])
   }
 
+  /**
+   * tests that an exception occurs if we try to parse an inputSource with no backing inputStream
+   */
   @Test def testDaffodilParseXMLReader_parse_inputSource_no_backing_stream(): Unit = {
     val xmlReader = dp.newXMLReaderInstance
     val input = new InputSource()
@@ -120,53 +186,59 @@ class TestSAXParseAPI {
     assertTrue(ioe.getMessage.contains("InputSource must be backed by InputStream"))
   }
 
+  /**
+   * tests that we can parse using an inputSource with a backing inputStream
+   */
   @Test def testDaffodilParseXMLReader_parse_inputSource_with_backing_stream(): Unit = {
-    val xmlReader = dp.newXMLReaderInstance
-    val baos = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baos)
-    xmlReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = testData.getBytes()
+    val (xmlReader: DFDL.DaffodilParseXMLReader,
+      baos: ByteArrayOutputStream,
+      inArray: Array[Byte]) = setupSAXParserTest(dp, testData)
     val bais = new ByteArrayInputStream(inArray)
     val input = new InputSource(bais)
     xmlReader.parse(input)
     val pr = xmlReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
+    val actualInfoset = scala.xml.XML.loadString(baos.toString)
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, scala.xml.XML.loadString(baos.toString))
+    assertEquals(expectedInfoset, actualInfoset)
   }
 
+  /**
+   * tests that we can parse using an inputStream
+   */
   @Test def testDaffodilParseXMLReader_parse_inputStream(): Unit = {
-    val xmlReader = dp.newXMLReaderInstance
-    val baos = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baos)
-    xmlReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = testData.getBytes()
+    val (xmlReader: DFDL.DaffodilParseXMLReader,
+      baos: ByteArrayOutputStream,
+      inArray: Array[Byte]) = setupSAXParserTest(dp, testData)
     val bais = new ByteArrayInputStream(inArray)
     xmlReader.parse(bais)
     val pr = xmlReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
+    val actualInfoset = scala.xml.XML.loadString(baos.toString)
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, scala.xml.XML.loadString(baos.toString))
+    assertEquals(expectedInfoset, actualInfoset)
   }
 
+  /**
+   * tests that we can parse using a byte array
+   */
   @Test def testDaffodilParseXMLReader_parse_byteArray(): Unit = {
-    val xmlReader = dp.newXMLReaderInstance
-    val baos = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baos)
-    xmlReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = testData.getBytes()
+    val (xmlReader: DFDL.DaffodilParseXMLReader,
+      baos: ByteArrayOutputStream,
+      inArray: Array[Byte]) = setupSAXParserTest(dp, testData)
     xmlReader.parse(inArray)
+    val actualInfoset = scala.xml.XML.loadString(baos.toString)
     val pr = xmlReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, scala.xml.XML.loadString(baos.toString))
+    assertEquals(expectedInfoset, actualInfoset)
   }
 
+  /**
+   * tests that the error handler is populated if we try to parse an empty input
+   */
   @Test def testDaffodilParseXMLReader_parse_errorHandler_empty_byteArray(): Unit = {
-    val xmlReader = dp.newXMLReaderInstance
-    val baos = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baos)
-    xmlReader.setContentHandler(parseOutputStreamContentHandler)
+    val (xmlReader: DFDL.DaffodilParseXMLReader, _, inArray: Array[Byte]) =
+      setupSAXParserTest(dp, "")
     val eh = new BuilderErrorHandler
     xmlReader.setErrorHandler(eh)
-    val inArray = "".getBytes()
     val spe = intercept[SAXParseException](
       xmlReader.parse(inArray)
     )
@@ -174,4 +246,395 @@ class TestSAXParseAPI {
     assertTrue(pr.isError)
     assertTrue(spe.getMessage.contains("Insufficient bits in data"))
   }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to false and the namespace prefix feature is set to true, for a data that generates an infoset
+   * with nested elements from different namespaces, including nil elements
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_prefixes_only1(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithNestedSchemas, qualifiedWithNestedSchemasData,
+      namespaces = false, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithNestedSchemasExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to false and the namespace prefix feature is set to true, qnames are populated for elements,
+   * but there not uris and localnames; attributes contain prefix mappings where the qname has the
+   * xmlns* and the value contains the associated uri. Regular elements are also provided by attributes
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_prefixes_only2(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpUnqualifiedNoNamespaces, unqualifiedNoNamespacesData,
+      namespaces = false, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(unqualifiedNoNamespacesExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to false and the namespace prefix feature is set to true., for data that generates an infoset
+   * with nested elements
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_prefixes_only3(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpUnqualifiedWithNestedQualified, unqualifiedWithNestedQualifiedData,
+      namespaces = false, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(unqualifiedWithNestedQualifiedExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to false and the namespace prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_prefixes_only4(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithDefaultNamespaceSchemas, qualifiedWithDefaultNamespaceData,
+      namespaces = false, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithDefaultNamespaceExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to false and the namespace prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_prefixes_only5(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithDefaultAndNestedSchemas, qualifiedWithDefaultAndNestedSchemasData,
+      namespaces = false, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithDefaultAndNestedSchemasExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix false is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_namespace_only1(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithNestedSchemas, qualifiedWithNestedSchemasData,
+      namespaces = true, namespacePrefixes = false)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithNestedSchemasExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_namespace_only2(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpUnqualifiedNoNamespaces, unqualifiedNoNamespacesData,
+      namespaces = true, namespacePrefixes = false
+    )
+    assertTrue(!pr.isError)
+    assertEquals(unqualifiedNoNamespacesExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix false is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_namespace_only3(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpUnqualifiedWithNestedQualified, unqualifiedWithNestedQualifiedData,
+      namespaces = true, namespacePrefixes = false)
+    assertTrue(!pr.isError)
+    assertEquals(unqualifiedWithNestedQualifiedExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix false is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_namespace_only4(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithDefaultNamespaceSchemas, qualifiedWithDefaultNamespaceData,
+      namespaces = true, namespacePrefixes = false)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithDefaultNamespaceExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix false is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_namespace_only5(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithDefaultAndNestedSchemas, qualifiedWithDefaultAndNestedSchemasData,
+      namespaces = true, namespacePrefixes = false)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithDefaultAndNestedSchemasExpectedInfoset, actualInfoset)
+  }
+
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces and the namespace
+   * prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_both1(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithNestedSchemas, qualifiedWithNestedSchemasData,
+      namespaces = true, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithNestedSchemasExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces and the namespace
+   * prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_both2(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpUnqualifiedNoNamespaces, unqualifiedNoNamespacesData,
+      namespaces = true, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(unqualifiedNoNamespacesExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces and the namespace
+   * prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_both3(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpUnqualifiedWithNestedQualified, unqualifiedWithNestedQualifiedData,
+      namespaces = true, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(unqualifiedWithNestedQualifiedExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces and the namespace
+   * prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_both4(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithDefaultNamespaceSchemas, qualifiedWithDefaultNamespaceData,
+      namespaces = true, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithDefaultNamespaceExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces and the namespace
+   * prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_parse_features_both5(): Unit = {
+    val (pr, actualInfoset) = saxParseWithFeatures(dpQualifiedWithDefaultAndNestedSchemas, qualifiedWithDefaultAndNestedSchemasData,
+      namespaces = true, namespacePrefixes = true)
+    assertTrue(!pr.isError)
+    assertEquals(qualifiedWithDefaultAndNestedSchemasExpectedInfoset, actualInfoset)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix feature is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_trace_features_default(): Unit = {
+    val baos = saxTraceParseWithFeatures(dpQualifiedWithNestedSchemas, nillableElementData,
+      namespaces = true, namespacePrefixes = false)
+    val actualOutput = baos.toString
+    val xsiUri = XMLUtils.XSI_NAMESPACE
+    val a02Uri = "http://a02.com"
+    val b02Uri = "http://b02.com"
+    val expectedOutput =
+      s"""startDocument
+        |startPrefixMapping(a02, $a02Uri)
+        |startPrefixMapping(b02, $b02Uri)
+        |startPrefixMapping(xsi, $xsiUri)
+        |startElement($b02Uri, seq, , Attributes())
+        |startElement($b02Uri, seq2, , Attributes())
+        |startElement($a02Uri, intx, , Attributes(($xsiUri,nil,,true)))
+        |endElement($a02Uri, intx, )
+        |endElement($b02Uri, seq2, )
+        |startElement($b02Uri, seq2, , Attributes())
+        |startElement($a02Uri, inty, , Attributes())
+        |character(Array(3), 0, 1)
+        |endElement($a02Uri, inty, )
+        |endElement($b02Uri, seq2, )
+        |startElement($b02Uri, seq2, , Attributes())
+        |startElement($b02Uri, inty, , Attributes())
+        |character(Array(4), 0, 1)
+        |endElement($b02Uri, inty, )
+        |endElement($b02Uri, seq2, )
+        |startElement($b02Uri, seq2, , Attributes())
+        |startElement($a02Uri, intx, , Attributes())
+        |character(Array(7), 0, 1)
+        |endElement($a02Uri, intx, )
+        |endElement($b02Uri, seq2, )
+        |endElement($b02Uri, seq, )
+        |endPrefixMapping(xsi)
+        |endPrefixMapping(b02)
+        |endPrefixMapping(a02)
+        |endDocument
+        |""".stripMargin
+    assertEquals(expectedOutput, actualOutput)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_trace_features_namespace_and_prefixes(): Unit = {
+    val baos = saxTraceParseWithFeatures(dpQualifiedWithNestedSchemas, nillableElementData,
+      namespaces = true, namespacePrefixes = true)
+    val actualOutput = baos.toString
+    val xsiUri = XMLUtils.XSI_NAMESPACE
+    val a02Uri = "http://a02.com"
+    val b02Uri = "http://b02.com"
+    val expectedOutput =
+      s"""startDocument
+         |startPrefixMapping(a02, $a02Uri)
+         |startPrefixMapping(b02, $b02Uri)
+         |startPrefixMapping(xsi, $xsiUri)
+         |startElement($b02Uri, seq, b02:seq, Attributes((,,xmlns:xsi,$xsiUri)(,,xmlns:b02,$b02Uri)(,,xmlns:a02,$a02Uri)))
+         |startElement($b02Uri, seq2, b02:seq2, Attributes())
+         |startElement($a02Uri, intx, a02:intx, Attributes(($xsiUri,nil,xsi:nil,true)))
+         |endElement($a02Uri, intx, a02:intx)
+         |endElement($b02Uri, seq2, b02:seq2)
+         |startElement($b02Uri, seq2, b02:seq2, Attributes())
+         |startElement($a02Uri, inty, a02:inty, Attributes())
+         |character(Array(3), 0, 1)
+         |endElement($a02Uri, inty, a02:inty)
+         |endElement($b02Uri, seq2, b02:seq2)
+         |startElement($b02Uri, seq2, b02:seq2, Attributes())
+         |startElement($b02Uri, inty, b02:inty, Attributes())
+         |character(Array(4), 0, 1)
+         |endElement($b02Uri, inty, b02:inty)
+         |endElement($b02Uri, seq2, b02:seq2)
+         |startElement($b02Uri, seq2, b02:seq2, Attributes())
+         |startElement($a02Uri, intx, a02:intx, Attributes())
+         |character(Array(7), 0, 1)
+         |endElement($a02Uri, intx, a02:intx)
+         |endElement($b02Uri, seq2, b02:seq2)
+         |endElement($b02Uri, seq, b02:seq)
+         |endPrefixMapping(xsi)
+         |endPrefixMapping(b02)
+         |endPrefixMapping(a02)
+         |endDocument
+         |""".stripMargin
+    assertEquals(expectedOutput, actualOutput)
+  }
+
+  /*
+  * tests that the output from the parser is as expected, when the namespaces feature is set
+  * to false and the namespace prefix feature is set to true.
+  */
+  @Test def testDaffodilParseXMLReader_trace_features_prefixes_only(): Unit = {
+    val baos = saxTraceParseWithFeatures(dpQualifiedWithNestedSchemas, nillableElementData,
+      namespaces = false, namespacePrefixes = true)
+    val actualOutput = baos.toString
+    val xsiUri = XMLUtils.XSI_NAMESPACE
+    val a02Uri = "http://a02.com"
+    val b02Uri = "http://b02.com"
+    val expectedOutput =
+      s"""startDocument
+         |startElement(, , b02:seq, Attributes((,,xmlns:xsi,$xsiUri)(,,xmlns:b02,$b02Uri)(,,xmlns:a02,$a02Uri)))
+         |startElement(, , b02:seq2, Attributes())
+         |startElement(, , a02:intx, Attributes((,,xsi:nil,true)))
+         |endElement(, , a02:intx)
+         |endElement(, , b02:seq2)
+         |startElement(, , b02:seq2, Attributes())
+         |startElement(, , a02:inty, Attributes())
+         |character(Array(3), 0, 1)
+         |endElement(, , a02:inty)
+         |endElement(, , b02:seq2)
+         |startElement(, , b02:seq2, Attributes())
+         |startElement(, , b02:inty, Attributes())
+         |character(Array(4), 0, 1)
+         |endElement(, , b02:inty)
+         |endElement(, , b02:seq2)
+         |startElement(, , b02:seq2, Attributes())
+         |startElement(, , a02:intx, Attributes())
+         |character(Array(7), 0, 1)
+         |endElement(, , a02:intx)
+         |endElement(, , b02:seq2)
+         |endElement(, , b02:seq)
+         |endDocument
+         |""".stripMargin
+    assertEquals(expectedOutput, actualOutput)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix feature is set to false.
+   */
+  @Test def testDaffodilParseXMLReader_trace_features_default_2(): Unit = {
+    val baos = saxTraceParseWithFeatures(dpUnqualifiedNoNamespaces, unqualifiedNoNamespacesData,
+      namespaces = true, namespacePrefixes = false)
+    val actualOutput = baos.toString
+    val xsiUri = XMLUtils.XSI_NAMESPACE
+    val expectedOutput =
+      s"""startDocument
+         |startPrefixMapping(xsi, $xsiUri)
+         |startElement(, x, , Attributes())
+         |startElement(, y, , Attributes())
+         |character(Array(w,o,r,l,d), 0, 5)
+         |endElement(, y, )
+         |startElement(, y, , Attributes())
+         |character(Array(n,o), 0, 2)
+         |endElement(, y, )
+         |startElement(, y, , Attributes(($xsiUri,nil,,true)))
+         |endElement(, y, )
+         |startElement(, y, , Attributes())
+         |character(Array(t,e,a), 0, 3)
+         |endElement(, y, )
+         |endElement(, x, )
+         |endPrefixMapping(xsi)
+         |endDocument
+         |""".stripMargin
+    assertEquals(expectedOutput, actualOutput)
+  }
+
+  /*
+   * tests that the output from the parser is as expected, when the namespaces feature is set
+   * to true and the namespace prefix feature is set to true.
+   */
+  @Test def testDaffodilParseXMLReader_trace_features_namespace_and_prefixes_2(): Unit = {
+    val baos = saxTraceParseWithFeatures(dpUnqualifiedNoNamespaces, unqualifiedNoNamespacesData,
+      namespaces = true, namespacePrefixes = true)
+    val actualOutput = baos.toString
+    val xsiUri = XMLUtils.XSI_NAMESPACE
+    val expectedOutput =
+      s"""startDocument
+         |startPrefixMapping(xsi, $xsiUri)
+         |startElement(, x, x, Attributes((,,xmlns:xsi,$xsiUri)))
+         |startElement(, y, y, Attributes())
+         |character(Array(w,o,r,l,d), 0, 5)
+         |endElement(, y, y)
+         |startElement(, y, y, Attributes())
+         |character(Array(n,o), 0, 2)
+         |endElement(, y, y)
+         |startElement(, y, y, Attributes(($xsiUri,nil,xsi:nil,true)))
+         |endElement(, y, y)
+         |startElement(, y, y, Attributes())
+         |character(Array(t,e,a), 0, 3)
+         |endElement(, y, y)
+         |endElement(, x, x)
+         |endPrefixMapping(xsi)
+         |endDocument
+         |""".stripMargin
+    assertEquals(expectedOutput, actualOutput)
+  }
+
+  /*
+  * tests that the output from the parser is as expected, when the namespaces feature is set
+  * to false and the namespace prefix feature is set to true.
+  */
+  @Test def testDaffodilParseXMLReader_trace_features_prefixes_only_2(): Unit = {
+    val baos = saxTraceParseWithFeatures(dpUnqualifiedNoNamespaces, unqualifiedNoNamespacesData,
+      namespaces = false, namespacePrefixes = true)
+    val actualOutput = baos.toString
+    val xsiUri = XMLUtils.XSI_NAMESPACE
+    val expectedOutput =
+      s"""startDocument
+         |startElement(, , x, Attributes((,,xmlns:xsi,$xsiUri)))
+         |startElement(, , y, Attributes())
+         |character(Array(w,o,r,l,d), 0, 5)
+         |endElement(, , y)
+         |startElement(, , y, Attributes())
+         |character(Array(n,o), 0, 2)
+         |endElement(, , y)
+         |startElement(, , y, Attributes((,,xsi:nil,true)))
+         |endElement(, , y)
+         |startElement(, , y, Attributes())
+         |character(Array(t,e,a), 0, 3)
+         |endElement(, , y)
+         |endElement(, , x)
+         |endDocument
+         |""".stripMargin
+    assertEquals(expectedOutput, actualOutput)
+  }
 }
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
index 347bbe2..27ec152 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXParseUnparseAPI.scala
@@ -20,74 +20,34 @@ package org.apache.daffodil.processor
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 
-import scala.xml.Elem
-
 import javax.xml.parsers.SAXParserFactory
-import org.apache.daffodil.compiler.Compiler
+import org.apache.daffodil.api.DFDL
 import org.apache.daffodil.infoset.ScalaXMLInfosetInputter
 import org.apache.daffodil.infoset.ScalaXMLInfosetOutputter
 import org.apache.daffodil.io.InputSourceDataInputStream
-import org.apache.daffodil.processors.DaffodilParseOutputStreamContentHandler
-import org.apache.daffodil.processors.DataProcessor
 import org.apache.daffodil.processors.ParseResult
-import org.apache.daffodil.util.SchemaUtils
 import org.apache.daffodil.xml.XMLUtils
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
 import org.junit.Test
 import org.xml.sax.InputSource
 
-object TestSAXParseUnparseAPI {
-  val testSchema: Elem = SchemaUtils.dfdlTestSchema(
-      <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
-      <dfdl:format ref="tns:GeneralFormat"/>,
-      <xs:element name="list" type="tns:example1"/>
-      <xs:complexType name="example1">
-        <xs:sequence>
-          <xs:element name="w" type="xs:int" dfdl:length="1" dfdl:lengthKind="explicit" maxOccurs="unbounded"/>
-        </xs:sequence>
-      </xs:complexType>
-  )
-  val testInfoset: Elem = <list xmlns="http://example.com"><w>9</w><w>1</w><w>0</w></list>
-  val testInfosetString: String = testInfoset.toString()
-  val testData = "910"
-
-  lazy val dp: DataProcessor = testDataProcessor(testSchema)
-
-  def testDataProcessor(testSchema: scala.xml.Elem, tunablesArg: Map[String, String] = Map.empty): DataProcessor = {
-    val schemaCompiler = Compiler().withTunables(tunablesArg)
-
-    val pf = schemaCompiler.compileNode(testSchema)
-    if (pf.isError) {
-      val msgs = pf.getDiagnostics.map { _.getMessage() }.mkString("\n")
-      fail("pf compile errors: " + msgs)
-    }
-    pf.sset.root.erd.preSerialization // force evaluation of all compile-time constructs
-    val dp = pf.onPath("/").asInstanceOf[DataProcessor]
-    if (dp.isError) {
-      val msgs = dp.getDiagnostics.map { _.getMessage() }.mkString("\n")
-      fail("dp compile errors: " + msgs)
-    }
-    dp
-  }
-}
-
 class TestSAXParseUnparseAPI {
-  import TestSAXParseUnparseAPI._
+  import TestSAXUtils._
 
+  /**
+   * Tests the case where we use SAX to parse data and SAX unparse to unparse the parsed data
+   */
   @Test def test_DaffodilParseXMLReader_parse_DaffodilUnparseContentHandler_unparse(): Unit = {
-    val parseXMLReader = dp.newXMLReaderInstance
-    val baosParse = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baosParse)
-    parseXMLReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = testData.getBytes()
+    val (parseXMLReader: DFDL.DaffodilParseXMLReader,
+    baosParse: ByteArrayOutputStream,
+    inArray: Array[Byte]) = setupSAXParserTest(dp, testData)
     val baisParse = new ByteArrayInputStream(inArray)
     val inputSourceParse = new InputSource(baisParse)
     parseXMLReader.parse(inputSourceParse)
     val pr = parseXMLReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, scala.xml.XML.loadString(baosParse.toString))
+    assertEquals(expectedInfoset, scala.xml.XML.loadString(baosParse.toString))
 
     val unparseXMLReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader
     unparseXMLReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, true)
@@ -104,6 +64,9 @@ class TestSAXParseUnparseAPI {
     assertEquals(testData, unparsedData)
   }
 
+  /**
+   * Tests the case where we use StAX to parse data and SAX to unparse the parse data
+   */
   @Test def test_DataProcessor_parse_DaffodilUnparseContentHandler_unparse(): Unit = {
     val inArray = testData.getBytes()
     val isdis = InputSourceDataInputStream(inArray)
@@ -111,7 +74,7 @@ class TestSAXParseUnparseAPI {
     val pr = dp.parse(isdis, sioo)
     val parsedData = sioo.getResult()
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, parsedData)
+    assertEquals(expectedInfoset, parsedData)
 
     val unparseXMLReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader
     unparseXMLReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, true)
@@ -128,6 +91,9 @@ class TestSAXParseUnparseAPI {
     assertEquals(testData, unparsedData)
   }
 
+  /**
+   * test the case where we use SAX to unparse data and SAX to parse the unparsed data
+   */
   @Test def test_DaffodilUnparseContentHandler_unparse_DaffodilParseXMLReader_parse(): Unit = {
     val unparseXMLReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader
     unparseXMLReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, true)
@@ -143,19 +109,20 @@ class TestSAXParseUnparseAPI {
     assertTrue(!ur.isError)
     assertEquals(testData, unparsedData)
 
-    val parseXMLReader = dp.newXMLReaderInstance
-    val baosParse = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baosParse)
-    parseXMLReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = baosUnparse.toByteArray
+    val (parseXMLReader: DFDL.DaffodilParseXMLReader,
+    baosParse: ByteArrayOutputStream,
+    inArray: Array[Byte]) = setupSAXParserTest(dp, baosUnparse.toString)
     val baisParse = new ByteArrayInputStream(inArray)
     val inputSourceParse = new InputSource(baisParse)
     parseXMLReader.parse(inputSourceParse)
     val pr = parseXMLReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, scala.xml.XML.loadString(baosParse.toString))
+    assertEquals(expectedInfoset, scala.xml.XML.loadString(baosParse.toString))
   }
 
+  /**
+   * tests the case where we use SAX to unparse data and StAX to parse the unparsed data
+   */
   @Test def test_DaffodilUnparseContentHandler_unparse_DataProcessor_parse(): Unit = {
     val unparseXMLReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader
     unparseXMLReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, true)
@@ -177,22 +144,20 @@ class TestSAXParseUnparseAPI {
     val pr = dp.parse(isdis, sioo)
     val parsedData = sioo.getResult()
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, parsedData)
+    assertEquals(expectedInfoset, parsedData)
   }
 
   @Test def test_DaffodilParseXMLReader_parse_DataProcessor_unparse(): Unit = {
-    val parseXMLReader = dp.newXMLReaderInstance
-    val baosParse = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baosParse)
-    parseXMLReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = testData.getBytes()
+    val (parseXMLReader: DFDL.DaffodilParseXMLReader,
+    baosParse: ByteArrayOutputStream,
+    inArray: Array[Byte]) = setupSAXParserTest(dp, testData)
     val baisParse = new ByteArrayInputStream(inArray)
     val inputSourceParse = new InputSource(baisParse)
     parseXMLReader.parse(inputSourceParse)
     val parsedNode = scala.xml.XML.loadString(baosParse.toString)
     val pr = parseXMLReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, parsedNode)
+    assertEquals(expectedInfoset, parsedNode)
 
     val baosUnparse = new ByteArrayOutputStream()
     val wbcUnparse = java.nio.channels.Channels.newChannel(baosUnparse)
@@ -206,21 +171,19 @@ class TestSAXParseUnparseAPI {
   @Test def test_DataProcessor_unparse_DaffodilParseXMLReader_parse(): Unit = {
     val baosUnparse = new ByteArrayOutputStream()
     val wbcUnparse = java.nio.channels.Channels.newChannel(baosUnparse)
-    val sii = new ScalaXMLInfosetInputter(testInfoset)
+    val sii = new ScalaXMLInfosetInputter(expectedInfoset)
     val ur = dp.unparse(sii, wbcUnparse)
     assertTrue(!ur.isError)
     assertEquals(testData, baosUnparse.toString)
 
-    val parseXMLReader = dp.newXMLReaderInstance
-    val baosParse = new ByteArrayOutputStream()
-    val parseOutputStreamContentHandler = new DaffodilParseOutputStreamContentHandler(baosParse)
-    parseXMLReader.setContentHandler(parseOutputStreamContentHandler)
-    val inArray = baosUnparse.toByteArray
+    val (parseXMLReader: DFDL.DaffodilParseXMLReader,
+    baosParse: ByteArrayOutputStream,
+    inArray: Array[Byte]) = setupSAXParserTest(dp, baosUnparse.toString)
     val baisParse = new ByteArrayInputStream(inArray)
     val inputSourceParse = new InputSource(baisParse)
     parseXMLReader.parse(inputSourceParse)
     val pr = parseXMLReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
     assertTrue(!pr.isError)
-    assertEquals(testInfoset, scala.xml.XML.loadString(baosParse.toString))
+    assertEquals(expectedInfoset, scala.xml.XML.loadString(baosParse.toString))
   }
 }
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUnparseAPI.scala b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUnparseAPI.scala
index afd3937..61175b2 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUnparseAPI.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUnparseAPI.scala
@@ -30,8 +30,12 @@ import org.xml.sax.InputSource
 import org.xml.sax.XMLReader
 
 class TestSAXUnparseAPI {
-  import TestSAXParseUnparseAPI._
+  import TestSAXUtils._
 
+  /**
+   * tests the base case of unparsing error free using SAX. Default for namespace features/prefixes
+   * is true/true for SAXParserFactory
+   */
   @Test def testUnparseContentHandler_unparse(): Unit = {
     val xmlReader: XMLReader = SAXParserFactory.newInstance.newSAXParser.getXMLReader
     val bao = new ByteArrayOutputStream()
@@ -53,13 +57,16 @@ class TestSAXUnparseAPI {
    */
   @Test def testUnparseContentHandler_unparse_saxUnparseEventBatchSize_0(): Unit = {
      val e = intercept[java.lang.IllegalArgumentException] {
-      testDataProcessor(testSchema, Map("saxUnparseEventBatchSize" -> "0"))
+      testDataProcessor(testSchema1, Map("saxUnparseEventBatchSize" -> "0"))
      }
      val eMsg = e.getMessage
      assertTrue(eMsg.contains("saxUnparseEventBatchSize"))
      assertTrue(eMsg.contains("0"))
   }
 
+  /**
+   * tests the case of unparsing with the namespace features/prefixes set to true/false
+   */
   @Test def testUnparseContentHandler_unparse_namespace_feature(): Unit = {
     val xmlReader: XMLReader = SAXParserFactory.newInstance.newSAXParser.getXMLReader
     val bao = new ByteArrayOutputStream()
@@ -75,6 +82,9 @@ class TestSAXUnparseAPI {
     assertEquals(testData, bao.toString)
   }
 
+  /**
+   * tests the case of unparsing with the namespace features/prefixes set to false/true
+   */
   @Test def testUnparseContentHandler_unparse_namespace_prefix_feature(): Unit = {
     val xmlReader: XMLReader = SAXParserFactory.newInstance.newSAXParser.getXMLReader
     val bao = new ByteArrayOutputStream()
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUtils.scala b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUtils.scala
new file mode 100644
index 0000000..5294b7a
--- /dev/null
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/processor/TestSAXUtils.scala
@@ -0,0 +1,356 @@
+/*
+ * 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,
+ * 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.daffodil.processor
+
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.OutputStream
+import java.io.OutputStreamWriter
+
+import scala.xml.Elem
+
+import org.apache.daffodil.api.DFDL
+import org.apache.daffodil.compiler.Compiler
+import org.apache.daffodil.processors.DaffodilParseOutputStreamContentHandler
+import org.apache.daffodil.processors.DataProcessor
+import org.apache.daffodil.processors.ParseResult
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.util.SchemaUtils
+import org.apache.daffodil.xml.XMLUtils
+import org.jdom2.input.sax.BuilderErrorHandler
+import org.junit.Assert.fail
+import org.xml.sax.Attributes
+import org.xml.sax.ContentHandler
+import org.xml.sax.Locator
+
+object TestSAXUtils {
+  lazy val testSchema1: Elem = SchemaUtils.dfdlTestSchema(
+      <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+      <dfdl:format ref="tns:GeneralFormat"/>,
+      <xs:element name="list" type="tns:example1"/>
+      <xs:complexType name="example1">
+        <xs:sequence>
+          <xs:element name="w" type="xs:int" dfdl:length="1" dfdl:lengthKind="explicit" maxOccurs="unbounded"/>
+        </xs:sequence>
+      </xs:complexType>
+  )
+  lazy val dp: DataProcessor = testDataProcessor(testSchema1)
+  lazy val expectedInfoset: Elem = <list xmlns="http://example.com"><w>9</w><w>1</w><w>0</w></list>
+  lazy val testInfosetString: String = expectedInfoset.toString()
+  lazy val testData: String = "910"
+
+
+  lazy val qualifiedWithNestedSchemasFile: File = new File(
+    Misc.getRequiredResource("/test/example_nested_namespaces_qualified.dfdl.xsd"))
+  lazy val qualifiedWithNestedSchemasElem: Elem = scala.xml.XML.loadFile(qualifiedWithNestedSchemasFile)
+  lazy val dpQualifiedWithNestedSchemas: DataProcessor = testDataProcessor(qualifiedWithNestedSchemasElem)
+  lazy val qualifiedWithNestedSchemasExpectedInfoset: Elem = {
+<b02:seq xmlns:xsi={XMLUtils.XSI_NAMESPACE} xmlns:b02="http://b02.com" xmlns:a02="http://a02.com">
+  <b02:seq2>
+    <a02:inty>3</a02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <b02:inty>4</b02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <a02:inty>2</a02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <a02:intx xsi:nil="true"/>
+  </b02:seq2>
+  <b02:seq2>
+    <b02:inty>1</b02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <b02:inty>44</b02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <a02:intx xsi:nil="true"/>
+  </b02:seq2>
+  <b02:seq2>
+    <b02:inty>643</b02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <a02:inty>3</a02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <a02:intx xsi:nil="true"/>
+  </b02:seq2>
+  <b02:seq2>
+    <a02:inty>5</a02:inty>
+  </b02:seq2>
+  <b02:seq2>
+    <b02:inty>1</b02:inty>
+  </b02:seq2>
+</b02:seq>
+  }
+  lazy val qualifiedWithNestedSchemasExpectedString: String = qualifiedWithNestedSchemasExpectedInfoset.toString()
+  lazy val qualifiedWithNestedSchemasData: String = "-3.*4.-2.^.*1.*44.^.*643.-3.^.-5.*1"
+
+  lazy val nillableElementExpectedInfoset: Elem = {
+    <b02:seq xmlns:xsi={ XMLUtils.XSI_NAMESPACE } xmlns:b02="http://b02.com" xmlns:a02="http://a02.com">
+      <b02:seq2>
+        <a02:intx xsi:nil="true"/>
+      </b02:seq2>
+      <b02:seq2>
+        <a02:inty>3</a02:inty>
+      </b02:seq2>
+      <b02:seq2>
+        <b02:inty>4</b02:inty>
+      </b02:seq2>
+      <b02:seq2>
+        <a02:intx>7</a02:intx>
+      </b02:seq2>
+    </b02:seq>
+  }
+  lazy val nillableElementExpectedString: String = nillableElementExpectedInfoset.toString()
+  lazy val nillableElementData: String = "^.-3.*4.7"
+
+  lazy val unqualifiedNoNamespacesFile: File = new File(
+    Misc.getRequiredResource("/test/example_no_targetnamespace.dfdl.xsd"))
+  lazy val unqualifiedNoNamespacesElem: Elem = scala.xml.XML.loadFile(unqualifiedNoNamespacesFile)
+  /**
+   * For an unqualified schemas with no targetnamespace and no default namespace, which means its
+   * elements are not in any namespace and there are no prefixes
+   * That schema references an element in a qualified namespace with a default
+   * namespace, which means it will have no prefix (default) but be in its default namespace
+   */
+  lazy val dpUnqualifiedNoNamespaces: DataProcessor = testDataProcessor(unqualifiedNoNamespacesElem)
+  lazy val unqualifiedNoNamespacesExpectedInfoset: Elem = {
+<x xmlns:xsi={ XMLUtils.XSI_NAMESPACE }>
+  <y>world</y>
+  <y>no</y>
+  <y xsi:nil="true"/>
+  <y>tea</y>
+</x>
+  }
+  lazy val unqualifiedNoNamespacesExpectedString: String = unqualifiedNoNamespacesExpectedInfoset.toString()
+  lazy val unqualifiedNoNamespacesData: String = "world.no.^.tea"
+
+  lazy val unqualifiedWithNestedQualifiedFile: File = new File(
+    Misc.getRequiredResource("/test/example_nested_namespaces_unqualified.dfdl.xsd"))
+  lazy val unqualifiedWithNestedQualifiedElem: Elem = scala.xml.XML.loadFile(unqualifiedWithNestedQualifiedFile)
+  /**
+   * For an unqualified schemas with a targetnamespace and no default namespace, which means its
+   * elements are in that targetnamespace, and only global elements need a prefix (unqualified).
+   * That schema references an element in a qualified namespace with a default
+   * namespace, which means it will have no prefix (default) but be in its default namespace
+   */
+  lazy val dpUnqualifiedWithNestedQualified: DataProcessor = testDataProcessor(unqualifiedWithNestedQualifiedElem)
+  lazy val unqualifiedWithNestedQualifiedExpectedInfoset: Elem = {
+<b02:a xmlns:xsi={ XMLUtils.XSI_NAMESPACE } xmlns:b02="http://b02.com" xmlns:c02="http://c02.com">
+  <b>
+    <c xmlns="http://c02.com">
+      <d>hello</d>
+    </c>
+  </b>
+  <b>
+    <c xmlns="http://c02.com">
+      <d xsi:nil="true"/>
+    </c>
+  </b>
+  <b>
+    <c xmlns="http://c02.com">
+      <d>bye</d>
+    </c>
+  </b>
+</b02:a>
+  }
+  lazy val unqualifiedWithNestedQualifiedExpectedString: String = unqualifiedWithNestedQualifiedExpectedInfoset.toString()
+  lazy val unqualifiedWithNestedQualifiedData: String = "hello.^.bye"
+
+  lazy val qualifiedWithDefaultNamespaceFile: File = new File(
+    Misc.getRequiredResource("/test/example_c02_targetnamespace_qualified.dfdl.xsd"))
+  lazy val qualifiedWithDefaultNamespaceElem: Elem = scala.xml.XML.loadFile(qualifiedWithDefaultNamespaceFile)
+  lazy val dpQualifiedWithDefaultNamespaceSchemas: DataProcessor = testDataProcessor(qualifiedWithDefaultNamespaceElem)
+  lazy val qualifiedWithDefaultNamespaceExpectedInfoset: Elem = {
+<c xmlns="http://c02.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <d>hello</d>
+</c>
+  }
+  lazy val qualifiedWithDefaultNamespaceExpectedString: String = qualifiedWithDefaultNamespaceExpectedInfoset.toString()
+  lazy val qualifiedWithDefaultNamespaceData: String = "hello"
+
+  lazy val qualifiedWithDefaultAndNestedSchemasFile: File = new File(
+    Misc.getRequiredResource("/test/example_nested_namespaces_qualified_with_default.dfdl.xsd"))
+  lazy val qualifiedWithDefaultAndNestedSchemasElem: Elem = scala.xml.XML.loadFile(qualifiedWithDefaultAndNestedSchemasFile)
+  lazy val dpQualifiedWithDefaultAndNestedSchemas: DataProcessor = testDataProcessor(qualifiedWithDefaultAndNestedSchemasElem)
+  lazy val qualifiedWithDefaultAndNestedSchemasExpectedInfoset: Elem = {
+<a  xmlns="http://b02.com" xmlns:xsi={XMLUtils.XSI_NAMESPACE} xmlns:c02="http://c02.com">
+  <b>
+    <c xmlns="http://c02.com">
+      <d>test</d>
+    </c>
+  </b>
+  <b>
+    <c xmlns="http://c02.com">
+      <d>ting</d>
+    </c>
+  </b>
+</a>
+  }
+  lazy val qualifiedWithDefaultAndNestedSchemasExpectedString: String = qualifiedWithDefaultAndNestedSchemasExpectedInfoset.toString()
+  lazy val qualifiedWithDefaultAndNestedSchemasData: String = "test.ting"
+
+
+
+  def testDataProcessor(testSchema: scala.xml.Elem, tunablesArg: Map[String, String] = Map.empty): DataProcessor = {
+    val schemaCompiler = Compiler().withTunables(tunablesArg)
+
+    val pf = schemaCompiler.compileNode(testSchema)
+    if (pf.isError) {
+      val msgs = pf.getDiagnostics.map { _.getMessage() }.mkString("\n")
+      fail("pf compile errors: " + msgs)
+    }
+    pf.sset.root.erd.preSerialization // force evaluation of all compile-time constructs
+    val dp = pf.onPath("/").asInstanceOf[DataProcessor]
+    if (dp.isError) {
+      val msgs = dp.getDiagnostics.map { _.getMessage() }.mkString("\n")
+      fail("dp compile errors: " + msgs)
+    }
+    dp
+  }
+
+  def saxParseWithFeatures(
+    dp: DataProcessor,
+    data: String,
+    namespaces: Boolean,
+    namespacePrefixes: Boolean): (ParseResult, scala.xml.Elem) = {
+    val (xmlReader: DFDL.DaffodilParseXMLReader,
+    baos: ByteArrayOutputStream,
+    inArray: Array[Byte]) = setupSAXParserTest(dp, data, pretty = true)
+    xmlReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, namespaces)
+    xmlReader.setFeature(XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE, namespacePrefixes)
+    xmlReader.parse(inArray)
+    val pr = xmlReader.getProperty(XMLUtils.DAFFODIL_SAX_URN_PARSERESULT).asInstanceOf[ParseResult]
+    val actualInfoset = scala.xml.XML.loadString(baos.toString)
+    (pr, actualInfoset)
+  }
+
+  def saxTraceParseWithFeatures(
+    dp: DataProcessor,
+    data: String,
+    namespaces: Boolean,
+    namespacePrefixes: Boolean): ByteArrayOutputStream = {
+    val (xmlReader: DFDL.DaffodilParseXMLReader,
+    baos: ByteArrayOutputStream,
+    inArray: Array[Byte]) = setupTraceSAXParserTest(dp, data, pretty = true)
+    xmlReader.setFeature(XMLUtils.SAX_NAMESPACES_FEATURE, namespaces)
+    xmlReader.setFeature(XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE, namespacePrefixes)
+    xmlReader.parse(inArray)
+    val traceContentHandler = xmlReader.getContentHandler.asInstanceOf[TestContentHandler]
+    traceContentHandler.fini()
+    baos
+  }
+
+  def setupSAXParserTest(dp: DFDL.DataProcessor, data: String, pretty: Boolean = false):
+    (DFDL.DaffodilParseXMLReader, ByteArrayOutputStream, Array[Byte]) = {
+    val xmlReader = dp.newXMLReaderInstance
+    val baos = new ByteArrayOutputStream()
+    val parseContentHandler = new DaffodilParseOutputStreamContentHandler(baos, pretty = pretty)
+    val eh = new BuilderErrorHandler
+    xmlReader.setErrorHandler(eh)
+    xmlReader.setContentHandler(parseContentHandler)
+    val inArray = data.getBytes()
+    (xmlReader, baos, inArray)
+  }
+
+  def setupTraceSAXParserTest(dp: DFDL.DataProcessor, data: String, pretty: Boolean = false):
+  (DFDL.DaffodilParseXMLReader, ByteArrayOutputStream, Array[Byte]) = {
+    val xmlReader = dp.newXMLReaderInstance
+    val baos = new ByteArrayOutputStream()
+    val parseContentHandler = new TestContentHandler(baos)
+    val eh = new BuilderErrorHandler
+    xmlReader.setErrorHandler(eh)
+    xmlReader.setContentHandler(parseContentHandler)
+    val inArray = data.getBytes()
+    (xmlReader, baos, inArray)
+  }
+
+}
+
+/**
+ * Test ContentHandler class that is used to test that XMLReader provides the expected input
+ * to ContentHandler classes
+ *
+ * @param out output Stream of choice that the trace will be written to
+ */
+class TestContentHandler(out: OutputStream) extends ContentHandler {
+  val sb: StringBuilder = new StringBuilder
+  private val writer = new OutputStreamWriter(out)
+  /**
+   * platform specific newline
+   */
+  private val newLine = System.lineSeparator()
+
+  override def startDocument(): Unit = writer.write(s"startDocument$newLine")
+
+  override def endDocument(): Unit = writer.write(s"endDocument$newLine")
+
+  override def startPrefixMapping(prefix: String, uri: String): Unit = {
+    writer.write(s"startPrefixMapping($prefix, $uri)$newLine")
+  }
+
+  override def endPrefixMapping(prefix: String): Unit = {
+    writer.write(s"endPrefixMapping($prefix)$newLine")
+  }
+
+  override def startElement(uri: String, localName: String, qName: String, atts: Attributes): Unit = {
+    writer.write(s"startElement($uri, $localName, $qName, ${attributesToString(atts)})$newLine")
+  }
+
+  override def endElement(uri: String, localName: String, qName: String): Unit = {
+    writer.write(s"endElement($uri, $localName, $qName)$newLine")
+  }
+
+  override def characters(ch: Array[Char], start: Int, length: Int): Unit = {
+    writer.write(s"character(${ch.mkString("Array(", ",", ")")}, $start, $length)$newLine")
+  }
+
+  override def setDocumentLocator(locator: Locator): Unit = {
+    // do nothing
+  }
+
+  override def ignorableWhitespace(ch: Array[Char], start: Int, length: Int): Unit = {
+    // do nothing
+  }
+
+  override def processingInstruction( target: String, data: String): Unit = {
+    // do nothing
+  }
+
+  override def skippedEntity(name: String): Unit = {
+    // do nothing
+  }
+
+  def attributesToString(atts: Attributes): String = {
+    sb.setLength(0)
+    val len = atts.getLength
+    sb ++= "Attributes("
+    for (i <- 0 until len) {
+      sb ++= s"(${atts.getURI(i)},${atts.getLocalName(i)},${atts.getQName(i)},${atts.getValue(i)})"
+    }
+    sb ++= ")"
+    sb.toString()
+  }
+
+  def fini(): Unit = {
+    writer.flush()
+  }
+
+}
diff --git a/daffodil-japi/src/main/java/org/apache/daffodil/japi/package-info.java b/daffodil-japi/src/main/java/org/apache/daffodil/japi/package-info.java
index 2c00eb1..1eaa6a0 100644
--- a/daffodil-japi/src/main/java/org/apache/daffodil/japi/package-info.java
+++ b/daffodil-japi/src/main/java/org/apache/daffodil/japi/package-info.java
@@ -200,6 +200,11 @@
  * }
  * </pre>
  *
+ * The value of the supported features cannot be changed during a parse, and the parse will run
+ * with the value of the features as they were when the parse was kicked off. To run a parse with
+ * different feature values, one must wait until the running parse finishes, set the feature values
+ * using the XMLReader's setFeature and run the parse again.
+ *
  * One can repeat calls to parse() using the same InputSourceDataInputStream to continue parsing
  * where the previous parse ended. For example:
  *
diff --git a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
index 99647e5..64aa384 100644
--- a/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
+++ b/daffodil-japi/src/test/java/org/apache/daffodil/example/TestJavaAPI.java
@@ -980,6 +980,9 @@ public class TestJavaAPI {
 
         org.jdom2.input.sax.SAXHandler contentHandler = new org.jdom2.input.sax.SAXHandler();
         SAXErrorHandlerForJAPITest errorHandler = new SAXErrorHandlerForJAPITest();
+        // since SAXHandler uses a blank prefix when the below isn't set to true, it introduces
+        // an undesired no-prefixed xmlns mapping
+        parseXMLReader.setFeature(SAX_NAMESPACE_PREFIXES_FEATURE, true);
         parseXMLReader.setContentHandler(contentHandler);
         parseXMLReader.setErrorHandler(errorHandler);
         parseXMLReader.setProperty(DaffodilParseXMLReader.DAFFODIL_SAX_URN_BLOBDIRECTORY(),
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/QNameBase.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/QNameBase.scala
index 4d35706..809faec 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/QNameBase.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/QNameBase.scala
@@ -225,7 +225,6 @@ trait QNameBase extends Serializable {
    * or by omitting one, are very common.
    */
   def prefix: Option[String]
-  def prefixOrNull: String = prefix.orNull
   def local: String
   def namespace: NS // No namespace is represented by the NoNamespace object.
 
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
index b517a90..872daa2 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
@@ -22,6 +22,7 @@ import java.nio.file.Files
 import java.nio.file.Paths
 import java.nio.file.StandardOpenOption
 
+import scala.annotation.tailrec
 import scala.collection.mutable
 import scala.collection.mutable.ArrayBuilder
 import scala.xml.NamespaceBinding
@@ -33,6 +34,7 @@ import org.apache.daffodil.calendar.DFDLDateTimeConversion
 import org.apache.daffodil.calendar.DFDLTimeConversion
 import org.apache.daffodil.exceptions._
 import org.apache.daffodil.schema.annotation.props.LookupLocation
+import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Misc
 
 /**
@@ -849,15 +851,25 @@ object XMLUtils {
       checkPrefixes,
       checkNamespaces)
     if (diffs.length > 0) {
+      val attributesProperty = if(checkPrefixes || checkNamespaces) {
+        "attributes not ignored for diff"
+      } else {
+        "attributes ignored for diff"
+      }
       throw new XMLDifferenceException("""
 Comparison failed.
-Expected (attributes stripped)
+Expected (attributes %s)
           %s
-Actual (attributes ignored for diff)
+Actual (attributes %s for diff)
           %s
 Differences were (path, expected, actual):
 %s""".format(
-        removeAttributes(expected).toString,
+        (if (checkPrefixes || checkNamespaces) "compared for diff"
+        else "stripped"),
+        (if (checkPrefixes || checkNamespaces) expected
+        else removeAttributes(expected).toString),
+        (if (checkPrefixes || checkNamespaces) "compared"
+        else "ignored"),
         actual,
         diffs.map { _.toString }.mkString("- ", "\n- ", "\n")))
     }
@@ -913,9 +925,6 @@ Differences were (path, expected, actual):
         val maybeType: Option[String] = Option(typeA.getOrElse(typeB.getOrElse(null)))
         val nilledA = a.attribute(XSI_NAMESPACE.toString, "nil")
         val nilledB = b.attribute(XSI_NAMESPACE.toString, "nil")
-        // we sort here, since sameElements is not order independent
-        val nsbACompare = nsbA.toString().trim.split(" ").sorted
-        val nsbBCompare = nsbB.toString().trim.split(" ").sorted
 
         if (labelA != labelB) {
           // different label
@@ -923,7 +932,7 @@ Differences were (path, expected, actual):
         } else if (checkPrefixes && prefixA != prefixB) {
           // different prefix
           List((zPath, prefixA, prefixB))
-        } else if (checkNamespaces && !nsbACompare.sameElements(nsbBCompare)) {
+        } else if (checkNamespaces && nsbA != nsbB ) {
           // different namespace bindings
           List((zPath, nsbA.toString(), nsbB.toString()))
         } else if (nilledA != nilledB) {
@@ -1255,6 +1264,46 @@ Differences were (path, expected, actual):
     sb.append(s)
     sb.append(";")
   }
+
+  /**
+   * Return a Maybe(URI) from NamespaceBinding based on some input prefix. There is a
+   * NamespaceBinding equivalent of this called getURI, but that does not handle the null case
+   * and will throw a NullPointerException when uri can't be found
+   *
+   * @param nsb NamespaceBinding we wish to search for the prefix's uri
+   * @param prefix Prefix whose URI we search through the NamespaceBinding for
+   * @return the uri string wrapped in a Maybe.One, or Maybe.Nope, if not found
+   */
+  @tailrec
+def maybeURI(nsb: NamespaceBinding, prefix: String): Maybe[String] = {
+    if (nsb == null) Maybe.Nope
+    else if (nsb.prefix == prefix) Maybe.One(nsb.uri)
+    else maybeURI(nsb.parent, prefix)
+  }
+
+  /**
+   * Return Maybe(prefix) from NamespaceBinding based on some input uri. There is a
+   * NamespaceBinding equivalent of this called gePrefix, but that does not handle the null case
+   * and will throw a NullPointerException when uri can't be found
+   *
+   * @param nsb NamespaceBinding we wish to search for the uri's prefix
+   * @param uri Prefix whose URI we search through the NamespaceBinding for
+   * @return the prefix string wrapped in a Maybe.One, or Maybe.Nope if not found or prefix is null
+   */
+  @tailrec
+  def maybePrefix(nsb: NamespaceBinding, uri: String): Maybe[String] = {
+
+    if (nsb == null) Maybe.Nope
+    else if (nsb.uri == uri) {
+      if (nsb.prefix == null) {
+        // the case of xmlns="some-uri"
+        Maybe.Nope
+      } else {
+        Maybe.One(nsb.prefix)
+      }
+    }
+    else maybePrefix(nsb.parent, uri)
+  }
 }
 
 trait GetAttributesMixin extends ThrowsSDE {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetOutputter.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetOutputter.scala
index 18c03de..f0f1ba1 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetOutputter.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/JDOMInfosetOutputter.scala
@@ -102,7 +102,7 @@ class JDOMInfosetOutputter extends InfosetOutputter
       if(diElement.erd.namedQName.namespace.isNoNamespace)
         new org.jdom2.Element(diElement.erd.name)
       else
-        new org.jdom2.Element(diElement.erd.name, diElement.erd.namedQName.prefixOrNull,
+        new org.jdom2.Element(diElement.erd.name, diElement.erd.prefix,
           diElement.erd.namedQName.namespace)
 
     if (isNilled(diElement)) {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetOutputter.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetOutputter.scala
index d2be4ba..f7df299 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetOutputter.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/SAXInfosetOutputter.scala
@@ -17,6 +17,8 @@
 
 package org.apache.daffodil.infoset
 
+import scala.xml.NamespaceBinding
+
 import org.apache.daffodil.api.DFDL
 import org.apache.daffodil.dpath.NodeInfo
 import org.apache.daffodil.xml.XMLUtils
@@ -24,9 +26,12 @@ import org.xml.sax.ContentHandler
 import org.xml.sax.SAXException
 import org.xml.sax.helpers.AttributesImpl
 
-class SAXInfosetOutputter(xmlReader: DFDL.DaffodilParseXMLReader)
+class SAXInfosetOutputter(xmlReader: DFDL.DaffodilParseXMLReader,
+  val namespacesFeature: Boolean,
+  val namespacePrefixesFeature: Boolean)
   extends InfosetOutputter
   with XMLInfosetOutputter {
+
   /**
    * Reset the internal state of this InfosetOutputter. This should be called
    * inbetween calls to the parse method.
@@ -137,64 +142,106 @@ class SAXInfosetOutputter(xmlReader: DFDL.DaffodilParseXMLReader)
 
   override def endArray(diArray: DIArray): Boolean = true // not applicable
 
-  private def createNilAttribute(): AttributesImpl = {
-    val attrs = new AttributesImpl()
-    attrs.addAttribute(XMLUtils.XSI_NAMESPACE, "nil", "xsi:nil", "", "true")
-    attrs
-  }
-
   private def doStartPrefixMapping(diElem: DIElement, contentHandler: ContentHandler): Unit = {
-    val nsbStart = diElem.erd.minimizedScope
-    val nsbEnd = if (diElem.isRoot) {
-      scala.xml.TopScope
-    } else {
-      diElem.diParent.erd.minimizedScope
-    }
+    val (nsbStart: NamespaceBinding, nsbEnd: NamespaceBinding) = getNsbStartAndEnd(diElem)
     var n = nsbStart
-    while (n != nsbEnd && n != null && n != scala.xml.TopScope) {
+    var mappingsList: Seq[(String, String)] = Seq()
+    while (n.ne(nsbEnd) && n.ne(null) && n.ne(scala.xml.TopScope)) {
       val prefix = if (n.prefix == null) "" else n.prefix
       val uri = if (n.uri == null) "" else n.uri
-      contentHandler.startPrefixMapping(prefix, uri)
+      // we generate a list here by prepending so can build the prefixMapping in the
+      // same order as it is in minimizedScope when we call startPrefixMapping; we do this because
+      // getPrefix in the contentHandler is order dependent
+      mappingsList +:= (prefix, uri)
       n = n.parent
     }
+    mappingsList.foreach{ case (prefix, uri) =>
+      contentHandler.startPrefixMapping(prefix, uri)
+    }
   }
 
   private def doEndPrefixMapping(diElem: DIElement, contentHandler: ContentHandler): Unit = {
-    val nsbStart = diElem.erd.minimizedScope
-    val nsbEnd = if (diElem.isRoot) {
-      scala.xml.TopScope
-    } else {
-      diElem.diParent.erd
-        .minimizedScope
-    }
+    val (nsbStart: NamespaceBinding, nsbEnd: NamespaceBinding) = getNsbStartAndEnd(diElem)
     var n = nsbStart
-    while (n != nsbEnd && n != null && n != scala.xml.TopScope) {
+    while (n.ne(nsbEnd) && n.ne(null) && n.ne(scala.xml.TopScope)) {
       val prefix = if (n.prefix == null) "" else n.prefix
       contentHandler.endPrefixMapping(prefix)
       n = n.parent
     }
   }
 
-  private def doStartElement(diElem: DIElement, contentHandler: ContentHandler): Unit = {
-    val (ns: String, elemName: String, qName: String) = getNameSpaceElemNameAndQName(diElem)
-    doStartPrefixMapping(diElem, contentHandler)
+  /**
+   * Add the prefixes and uris from the element's NamespaceBinding to Attributes,
+   * when namespacePrefixes feature is true
+   */
+  private def doAttributesPrefixMapping(diElem: DIElement, attrs: AttributesImpl): AttributesImpl = {
+    val (nsbStart: NamespaceBinding, nsbEnd: NamespaceBinding) = getNsbStartAndEnd(diElem)
+    var n = nsbStart
+    while (n.ne(nsbEnd) && n.ne(null) && n.ne(scala.xml.TopScope)) {
+      val prefix = if (n.prefix == null) "xmlns" else s"xmlns:${n.prefix}"
+      val uri = if (n.uri == null) "" else n.uri
+      // uri and localname are always empty for NamespaceBinding attributes
+      attrs.addAttribute("", "", prefix, "CDATA", uri)
+      n = n.parent
+    }
+    attrs
+  }
 
-    val attrs = if (isNilled(diElem)) {
-      createNilAttribute()
+  private def getNsbStartAndEnd(diElem: DIElement) = {
+    val nsbStart = diElem.erd.minimizedScope
+    val nsbEnd = if (diElem.isRoot) {
+      scala.xml.TopScope
     } else {
-      new AttributesImpl()
+      diElem.diParent.erd.minimizedScope
     }
+    (nsbStart, nsbEnd)
+  }
 
-    contentHandler.startElement(ns, elemName, qName, attrs)
+  private def doStartElement(diElem: DIElement, contentHandler: ContentHandler): Unit = {
+    val (ns: String, localName: String, qName: String) = getNamespaceLocalNameAndQName(diElem)
+    val attrs = new AttributesImpl()
+    val elemUri: String = if (namespacesFeature) ns else ""
+    val elemLocalName: String = if (namespacesFeature) localName else ""
+    val elemQname: String = if (namespacePrefixesFeature) qName else ""
+
+    if (namespacesFeature) {
+      // only when this feature is true do we use prefix mappings
+      doStartPrefixMapping(diElem, contentHandler)
+    }
+
+    if (namespacePrefixesFeature) {
+      // handle prefix attribute
+      doAttributesPrefixMapping(diElem, attrs)
+    }
+
+    // handle xsi:nil attribute
+    if (diElem.isNilled) {
+      val isNilled = "true"
+      val nType: String = "CDATA"
+      val nValue: String = isNilled
+      val nQname = if (namespacePrefixesFeature) "xsi:nil" else ""
+      val nUri: String = if (namespacesFeature) XMLUtils.XSI_NAMESPACE else ""
+      val nLocalName: String = if (namespacesFeature) "nil" else ""
+
+      attrs.addAttribute(nUri, nLocalName, nQname, nType, nValue)
+    }
+
+    contentHandler.startElement(elemUri, elemLocalName, elemQname, attrs)
   }
 
   private def doEndElement (diElem: DIElement, contentHandler: ContentHandler): Unit = {
-    val (ns: String, elemName: String, qName: String) = getNameSpaceElemNameAndQName(diElem)
-    contentHandler.endElement(ns, elemName, qName)
-    doEndPrefixMapping(diElem, contentHandler)
+    val (ns: String, localName: String, qName: String) = getNamespaceLocalNameAndQName(diElem)
+    val elemUri: String = if (namespacesFeature) ns else ""
+    val elemLocalName = if (namespacesFeature) localName else ""
+    val elemQname = if (namespacePrefixesFeature) qName else ""
+
+    contentHandler.endElement(elemUri, elemLocalName, elemQname)
+
+    // only when this feature is true do we use prefix mappings
+    if (namespacesFeature) doEndPrefixMapping(diElem, contentHandler)
   }
 
-  private def getNameSpaceElemNameAndQName(
+  private def getNamespaceLocalNameAndQName(
     diElem: DIElement): (String, String, String) = {
     val ns: String =
       if (diElem.erd.namedQName.namespace.isNoNamespace) {
@@ -203,7 +250,7 @@ class SAXInfosetOutputter(xmlReader: DFDL.DaffodilParseXMLReader)
         diElem.erd.namedQName.namespace.toString
       }
     val elemName = diElem.erd.namedQName.local
-    val qName = diElem.erd.namedQName.toQNameString
+    val qName = diElem.erd.prefixedName
     (ns, elemName, qName)
   }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
index 63c330b..2e037f5 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/ScalaXMLInfosetOutputter.scala
@@ -94,7 +94,7 @@ class ScalaXMLInfosetOutputter(showFormatInfo: Boolean = false, showFreedInfo: B
 
     val elem =
       scala.xml.Elem(
-        diSimple.erd.namedQName.prefixOrNull,
+        diSimple.erd.prefix,
         diSimple.erd.name,
         attributes,
         diSimple.erd.minimizedScope,
@@ -122,7 +122,7 @@ class ScalaXMLInfosetOutputter(showFormatInfo: Boolean = false, showFreedInfo: B
 
     val elem =
       scala.xml.Elem(
-        diComplex.erd.namedQName.prefixOrNull,
+        diComplex.erd.prefix,
         diComplex.erd.name,
         attributes,
         diComplex.erd.minimizedScope,
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetOutputter.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetOutputter.scala
index a2487dc..4925871 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetOutputter.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/infoset/XMLTextInfosetOutputter.scala
@@ -48,7 +48,7 @@ class XMLTextInfosetOutputter private (writer: java.io.Writer, pretty: Boolean,
   }
 
   private def outputTagName(elem: DIElement): Unit = {
-    val prefix = elem.erd.namedQName.prefixOrNull
+    val prefix = elem.erd.prefix
     if (prefix != null && prefix != "") {
       writer.write(prefix)
       writer.write(":")
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseOutputStreamContentHandler.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseOutputStreamContentHandler.scala
index 3255c03..0c71a5f 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseOutputStreamContentHandler.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseOutputStreamContentHandler.scala
@@ -22,17 +22,44 @@ import java.io.OutputStreamWriter
 
 import scala.xml.NamespaceBinding
 
-import org.apache.daffodil.infoset.XMLInfosetOutputter
+import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.util.Indentable
+import org.apache.daffodil.util.MStackOf
 import org.apache.daffodil.util.MStackOfBoolean
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.Maybe.Nope
+import org.apache.daffodil.xml.XMLUtils
 import org.xml.sax.Attributes
 import org.xml.sax.ContentHandler
 import org.xml.sax.Locator
 
+/**
+ * ContentHandler implementation that receives SAX events from DaffodilParseXMLReader to output
+ * XML to the specified outputStream. Depending on the features set in the XMLReader, it uses either
+ * prefixMappings or attributes to determine the prefix of the XML element. This means it will always
+ * try to find and print a prefix if an element has a URI.
+ *
+ * @param out outputStream object to write generated XML to
+ * @param pretty boolean to pretty print XML if true, or not if false
+ */
 class DaffodilParseOutputStreamContentHandler(out: OutputStream, pretty: Boolean = false)
-  extends ContentHandler with Indentable with XMLInfosetOutputter {
+  extends ContentHandler with Indentable {
   private val writer = new OutputStreamWriter(out)
-  private var prefixMapping: NamespaceBinding = null
+  /**
+   * represents the currently active prefix mappings (i.e all mappings include from parent element),
+   * which is usefully for doing lookups
+   */
+  private var activePrefixMapping: NamespaceBinding = null
+  /**
+   * represents only the prefix mapping of the current element. We use this to generate the prefix mappings
+   * when outputting the element tag
+   */
+  private var currentElementPrefixMapping: NamespaceBinding = null
+  /**
+   * used to maintain the correct scope of activePrefixMapping throughout processing. It is also used
+   * to reset the activePrefixMapping after processing each element.
+   */
+  private lazy val activePrefixMappingContextStack = new MStackOf[NamespaceBinding]
   private val outputNewlineStack: MStackOfBoolean = {
     val s = MStackOfBoolean()
     s.push(false)
@@ -45,7 +72,9 @@ class DaffodilParseOutputStreamContentHandler(out: OutputStream, pretty: Boolean
   def reset(): Unit = {
     resetIndentation()
     writer.flush()
-    prefixMapping = null
+    activePrefixMapping = null
+    currentElementPrefixMapping = null
+    activePrefixMappingContextStack.clear()
     outputNewlineStack.clear()
     outputNewlineStack.push(false) //to match initialization state
     out.flush()
@@ -66,13 +95,80 @@ class DaffodilParseOutputStreamContentHandler(out: OutputStream, pretty: Boolean
 
   override def startPrefixMapping(prefix: String, uri: String): Unit = {
     val _prefix = if (prefix == "") null else prefix
-    prefixMapping = NamespaceBinding(_prefix, uri, prefixMapping)
+    activePrefixMapping = NamespaceBinding(_prefix, uri, activePrefixMapping)
+    currentElementPrefixMapping = NamespaceBinding(_prefix, uri, currentElementPrefixMapping)
   }
 
   override def endPrefixMapping(prefix: String): Unit = {
     // do nothing
   }
 
+  /**
+   * Uses Attributes, which is passed in to the startElement callback, to gather element attributes
+   * or in the case where namespacePrefixes is true, prefix mappings. Any new prefix mappings are used
+   * to update the activePrefixMapping and currentElementPrefixMapping.
+   *
+   * @return sequence of string attribute=val pairings
+   */
+  def processAttributes(atts: Attributes): Seq[String] = {
+    var attrPairings: Seq[String] = Seq()
+    var i = 0
+    var newMappingsList: Seq[(String, String)] = Seq()
+    while (i < atts.getLength) {
+      val qName = atts.getQName(i)
+      val attrVal =  atts.getValue(i)
+      if (qName.nonEmpty) {
+        // if qName is populated; as in when namespacePrefixes == true
+        // describing namespace mapping
+        if (qName.startsWith("xmlns:") || qName == "xmlns") {
+          // get prefix
+          val pre = if (qName.startsWith("xmlns:")) {
+            qName.substring(6)
+          } else {
+            null
+          }
+          // we make this call to check if the prefix already exists. If it doesn't exist, we get a
+          // Nope, so we can add it to our list, but if it does exist, nothing happens and it doesn't
+          // get re-added and we instead proceed to the next item in Attributes
+          val maybeUri = XMLUtils.maybeURI(activePrefixMapping, pre)
+          if (maybeUri.isEmpty || maybeUri.get != attrVal) { // not found yet, add it
+            newMappingsList +:= (pre, attrVal)
+          }
+        } else {
+          // regular attribute with qname such as xsi:nil
+          attrPairings +:= s""" ${qName}="${attrVal}""""
+        }
+      } else {
+        // no qname, so namespacePrefixes == false, which means we get no
+        // prefix mappings in the attributes, only regular attributes such as xsi:nil
+        // though not in qname form
+        val uri = atts.getURI(i)
+        val localName = atts.getLocalName(i)
+        // prefixed attribute, not prefix mapping, as they only show up as qnames
+        if (uri.nonEmpty && localName.nonEmpty) {
+          val maybePrefix = XMLUtils.maybePrefix(activePrefixMapping, uri)
+          // found a prefix; add to attribute pairings
+          if (maybePrefix.isDefined) {
+            val prefix = maybePrefix.get
+            attrPairings +:= s""" $prefix:$localName="${attrVal}""""
+          } else {
+            // if an attribute has a URI, we must have a prefix, even if it is null
+           Assert.invariantFailed("Cannot have URI with no prefix mapping")
+          }
+        } else {
+          // non prefixed attribute don't exist in Daffodil
+          Assert.invariantFailed("Cannot have an attribute with no qname, uri or localname")
+        }
+      }
+      i += 1
+    }
+    newMappingsList.foreach{ case (prefix, uri) =>
+      activePrefixMapping = NamespaceBinding(prefix, uri, activePrefixMapping)
+      currentElementPrefixMapping = NamespaceBinding(prefix, uri, currentElementPrefixMapping)
+    }
+    attrPairings.reverse
+  }
+
   override def startElement(
     uri: String, localName: String, qName: String, atts: Attributes): Unit = {
     // the pop/true removes whatever is on the stack which is our previous guess for whether we
@@ -84,21 +180,34 @@ class DaffodilParseOutputStreamContentHandler(out: OutputStream, pretty: Boolean
       writer.write(System.lineSeparator())
       outputIndentation(writer)
     }
+
+    /**
+     * represents the attributes for the current element. We use this to generate the attributes list
+     * within the start tag
+     */
+    val currentElementAttributes = processAttributes(atts)
+    // we always push, but activePrefixMapping won't always be populated with new information
+    // from startPrefixMapping or processAttributes
+    activePrefixMappingContextStack.push(activePrefixMapping)
+
     // handle start of tag
     writer.write("<")
-    writer.write(qName)
-    // handle attributes
-    for (i <- 0 until atts.getLength) {
-      val attsValue = atts.getValue(i)
-      val attsQName = atts.getQName(i)
-      writer.write(s""" $attsQName="$attsValue"""")
-    }
-    // handle namespaces
-    if (prefixMapping != null) {
-      val pm = prefixMapping.toString()
+    outputTagName(uri, localName, qName, Some(atts))
+
+    // this contains only xmlns prefixes and are populated via the start/endPrefixMappings
+    // or Attributes via processAttributes()
+    if (currentElementPrefixMapping != null) {
+      val pm = currentElementPrefixMapping.toString()
       writer.write(pm)
-      prefixMapping = null
+      currentElementPrefixMapping = null
     }
+
+    // handles attributes from the Attributes object. Example attributes is xsi:nil
+    if (currentElementAttributes.nonEmpty) {
+      val attrs = currentElementAttributes.mkString(" ")
+      writer.write(attrs)
+    }
+
     // handle end of tag
     writer.write(">")
     incrementIndentation()
@@ -107,6 +216,29 @@ class DaffodilParseOutputStreamContentHandler(out: OutputStream, pretty: Boolean
     outputNewlineStack.push(false)
   }
 
+  private def outputTagName(uri: String, localName: String, qName: String, atts: Maybe[Attributes] = Nope): Unit = {
+    val tagName = {
+      if (qName.nonEmpty) {
+        // namespacePrefixes == true, so qName is populated
+        qName
+      } else {
+        // namespacePrefixes == false, so uri and localName are populated, but not qname
+        // and attributes don't have prefix mapping information
+        // if we have no qName, we need to use activePrefixMapping to get the prefix of the uri
+        // to build the qname
+        val sanitizedUri = if (uri.isEmpty) null else uri
+        val maybePrefix = XMLUtils.maybePrefix(activePrefixMapping, sanitizedUri)
+        if (maybePrefix.isDefined) {
+          val pre = maybePrefix.get
+          s"$pre:$localName"
+        } else {
+          localName
+        }
+      }
+    }
+    writer.write(tagName)
+  }
+
   override def endElement(uri: String, localName: String, qName: String): Unit = {
     decrementIndentation()
     if (outputNewline) {
@@ -116,9 +248,20 @@ class DaffodilParseOutputStreamContentHandler(out: OutputStream, pretty: Boolean
       }
     }
     writer.write("</")
-    writer.write(qName)
+    outputTagName(uri, localName, qName)
     writer.write(">")
     outputNewlineStack.pop()
+
+    Assert.invariant(!activePrefixMappingContextStack.isEmpty)
+    // throw out current prefix mapping context as we're done with it
+    activePrefixMappingContextStack.pop
+
+    // set the activePrefixMapping to the next mapping in the stack if the stack isn't empty
+    if (activePrefixMappingContextStack.isEmpty) {
+      activePrefixMapping = null
+    } else {
+      activePrefixMapping = activePrefixMappingContextStack.top
+    }
   }
 
   override def characters(ch: Array[Char], start: Int, length: Int): Unit = {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseXMLReader.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseXMLReader.scala
index 71c98af..8bc29d4 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseXMLReader.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DaffodilParseXMLReader.scala
@@ -22,8 +22,6 @@ import java.io.InputStream
 import java.nio.file.Path
 import java.nio.file.Paths
 
-import scala.collection.mutable
-
 import org.apache.daffodil.api.DFDL
 import org.apache.daffodil.exceptions.SchemaFileLocation
 import org.apache.daffodil.infoset.SAXInfosetOutputter
@@ -34,44 +32,44 @@ import org.xml.sax.DTDHandler
 import org.xml.sax.EntityResolver
 import org.xml.sax.ErrorHandler
 import org.xml.sax.InputSource
+import org.xml.sax.SAXException
 import org.xml.sax.SAXNotRecognizedException
 import org.xml.sax.SAXNotSupportedException
 import org.xml.sax.SAXParseException
 
+/**
+ * XMLReader implementation that interfaces with the DataProcessor and SAXInfosetOutputter to parse
+ * data into an XML infoset
+ *
+ * @param dp dataprocessor object that will be used to call the parse
+ */
 class DaffodilParseXMLReader(dp: DataProcessor) extends DFDL.DaffodilParseXMLReader {
   private var contentHandler: ContentHandler = _
   private var errorHandler: ErrorHandler = _
   private var dtdHandler: DTDHandler = _
   private var entityResolver: EntityResolver = _
-  var saxParseResultPropertyValue: ParseResult = _
-  var saxBlobDirectoryPropertyValue: Path = Paths.get(System.getProperty("java.io.tmpdir"))
-  var saxBlobPrefixPropertyValue: String = "daffodil-sax-"
-  var saxBlobSuffixPropertyValue: String = ".blob"
-
-  private val featureMap = mutable.Map[String, Boolean](
-    XMLUtils.SAX_NAMESPACES_FEATURE -> false,
-    XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE -> false
-  )
+  private var saxParseResultPropertyValue: ParseResult = _
+  private var saxBlobDirectoryPropertyValue: Path = Paths.get(System.getProperty("java.io.tmpdir"))
+  private var saxBlobPrefixPropertyValue: String = "daffodil-sax-"
+  private var saxBlobSuffixPropertyValue: String = ".blob"
+  private var saxNamespaceFeatureValue: Boolean = true
+  private var saxNamespacePrefixesFeatureValue: Boolean = false
 
   override def getFeature(name: String): Boolean = {
-    if (name == XMLUtils.SAX_NAMESPACES_FEATURE ||
-      name == XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE) {
-      featureMap(name)
-    } else {
-      throw new SAXNotRecognizedException("Feature unsupported: " + name + ".\n" +
-        "Supported features are: " + featureMap.keys.mkString(", ")
-      )
+    name match {
+      case XMLUtils.SAX_NAMESPACES_FEATURE => saxNamespaceFeatureValue
+      case XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE => saxNamespacePrefixesFeatureValue
+      case _ =>
+        throwFeatureSAXNotRecogizedException(name, "Feature unsupported")
     }
   }
 
   override def setFeature(name: String, value: Boolean): Unit = {
-    if (name == XMLUtils.SAX_NAMESPACES_FEATURE ||
-      name == XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE) {
-      featureMap(name) = value
-    } else {
-      throw new SAXNotRecognizedException("Feature unsupported: " + name + ".\n" +
-        "Supported features are: " + featureMap.keys.mkString(", ")
-      )
+    name match {
+      case XMLUtils.SAX_NAMESPACES_FEATURE => saxNamespaceFeatureValue = value
+      case XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE => saxNamespacePrefixesFeatureValue = value
+      case _ =>
+        throwFeatureSAXNotRecogizedException(name, "Feature unsupported")
     }
   }
 
@@ -144,7 +142,18 @@ class DaffodilParseXMLReader(dp: DataProcessor) extends DFDL.DaffodilParseXMLRea
   }
 
   def parse(isdis: InputSourceDataInputStream): Unit = {
-    val sio = createSAXInfosetOutputter(this)
+    // validate that the features are not false/false
+    if (!saxNamespaceFeatureValue && !saxNamespacePrefixesFeatureValue) {
+      throw new SAXException("Illegal State: Namespaces and NamespacePrefixes features cannot both be false")
+    }
+    // creates SAXInfosetOutputter object and calls setBlobAttributes on it
+    val sio = new SAXInfosetOutputter(this,
+      saxNamespaceFeatureValue,
+      saxNamespacePrefixesFeatureValue)
+    sio.setBlobAttributes(saxBlobDirectoryPropertyValue,
+      saxBlobPrefixPropertyValue,
+      saxBlobSuffixPropertyValue
+    )
     val pr = dp.parse(isdis, sio)
     saxParseResultPropertyValue = pr.asInstanceOf[ParseResult]
     handleDiagnostics(pr)
@@ -188,22 +197,10 @@ class DaffodilParseXMLReader(dp: DataProcessor) extends DFDL.DaffodilParseXMLRea
     }
   }
 
-  /**
-   * Creates SAXInfosetOutputter object and attempts to setBlobAttributes on it if
-   * it has at least the blobDirectory property set
-   *
-   * @return SAXInfosetOutputter object with or without blob Attributes set
-   */
-  private def createSAXInfosetOutputter(xmlReader: DaffodilParseXMLReader): SAXInfosetOutputter = {
-    val sioo = new SAXInfosetOutputter(xmlReader)
-    val siof = try {
-      sioo.setBlobAttributes(saxBlobDirectoryPropertyValue, saxBlobPrefixPropertyValue,
-        saxBlobSuffixPropertyValue
-      )
-      sioo
-    } catch {
-      case e: SAXNotSupportedException => sioo
-    }
-    siof
+  def throwFeatureSAXNotRecogizedException(name: String, message: String): Boolean = {
+    throw new SAXNotRecognizedException(message + ": " + name + ".\n" +
+      "Supported features are: " +
+      Seq(XMLUtils.SAX_NAMESPACES_FEATURE,
+        XMLUtils.SAX_NAMESPACE_PREFIXES_FEATURE).mkString(", "))
   }
 }
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
index 8e941dd..6e93846 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
@@ -680,9 +680,11 @@ sealed class ElementRuntimeData(
 
   def isComplexType = !isSimpleType
 
+  def prefix = this.minimizedScope.getPrefix(namedQName.namespace)
+
   def prefixedName = {
-    if (namedQName.prefixOrNull != null) {
-      namedQName.prefixOrNull + ":" + name
+    if (prefix != null) {
+      prefix + ":" + name
     } else {
       name
     }
diff --git a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/package.scala b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/package.scala
index 47b1726..0a5076c 100644
--- a/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/package.scala
+++ b/daffodil-sapi/src/main/scala/org/apache/daffodil/sapi/package.scala
@@ -180,6 +180,11 @@ package org.apache.daffodil
  * }
  * }}}
  *
+ * The value of the supported features cannot be changed during a parse, and the parse will run
+ * with the value of the features as they were when the parse was kicked off. To run a parse with
+ * different feature values, one must wait until the running parse finishes, set the feature values
+ * using the XMLReader's setFeature and run the parse again.
+ *
  * One can repeat calls to parse() using the same InputSourceDataInputStream to continue parsing
  * where the previous parse ended. For example:
  *
diff --git a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
index 45791ba..745d4e2 100644
--- a/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
+++ b/daffodil-sapi/src/test/scala/org/apache/daffodil/example/TestScalaAPI.scala
@@ -1003,6 +1003,9 @@ class TestScalaAPI {
 
     val outputContentHandler = new org.jdom2.input.sax.SAXHandler()
     val errorHandler = new SAXErrorHandlerForSAPITest()
+    // since SAXHandler uses a blank prefix when the below isn't set to true, it introduces
+    // a the no-prefixed xmlns mapping
+    parseXMLReader.setFeature(SAX_NAMESPACE_PREFIXES_FEATURE, true)
     parseXMLReader.setContentHandler(outputContentHandler)
     parseXMLReader.setErrorHandler(errorHandler)
     parseXMLReader.setProperty(DaffodilParseXMLReader.DAFFODIL_SAX_URN_BLOBDIRECTORY,
diff --git a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetOutputter.scala b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetOutputter.scala
index 83dc0ae..0259c79 100644
--- a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetOutputter.scala
+++ b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/TDMLInfosetOutputter.scala
@@ -104,6 +104,8 @@ class TDMLInfosetOutputter() extends InfosetOutputter {
 
   def getResult() = scalaOut.getResult
 
+  def getXmlString() = xmlStream.toString
+
   def toInfosetInputter() = {
     val scalaIn = new ScalaXMLInfosetInputter(scalaOut.getResult)
     val jdomIn = new JDOMInfosetInputter(jdomOut.getResult)
diff --git a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala
index 29f1452..2ab8437 100644
--- a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala
+++ b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala
@@ -379,14 +379,18 @@ class DaffodilTDMLDFDLProcessor private (private var dp: DataProcessor) extends
   }
 
   def verifySameParseOutput(dpOutputter: TDMLInfosetOutputter, outputStream: ByteArrayOutputStream): Unit = {
-    val dpParseOutput = dpOutputter.getResult()
+    val dpParseOutputString = dpOutputter.getXmlString()
     val saxParseOutputString = outputStream.toString
-    val saxParseOutput = scala.xml.XML.loadString(saxParseOutputString)
+    val saxParseXMLNodeOutput = scala.xml.XML.loadString(saxParseOutputString)
+    // scala.xml.XML.loadString reverses the order of the namespace mappings, so we call it for the
+    // dpParseXMLNodeOutput as well so the reversal is mirrored and we can do a proper prefixes and namespaces
+    // comparison. dpOutputter.getOutput returns it in the right order, which is why we don't use it
+    val dpParseXMLNodeOutputReloaded = scala.xml.XML.loadString(dpParseOutputString)
 
     try {
       XMLUtils.compareAndReport(
-        dpParseOutput,
-        saxParseOutput,
+        dpParseXMLNodeOutputReloaded,
+        saxParseXMLNodeOutput,
         checkNamespaces = true,
         checkPrefixes = true)
     } catch {