diff --git a/rdf4h.cabal b/rdf4h.cabal
--- a/rdf4h.cabal
+++ b/rdf4h.cabal
@@ -1,5 +1,5 @@
 name:            rdf4h
-version:         4.0.1
+version:         4.0.2
 synopsis:        A library for RDF processing in Haskell
 description:
   'RDF for Haskell' is a library for working with RDF in Haskell.
@@ -62,7 +62,6 @@
                  , hashable
                  , deepseq
                  , binary
-                 , hgal
                  , parsers
                  , mtl
                  , network-uri >= 2.6
diff --git a/src/Data/RDF.hs b/src/Data/RDF.hs
--- a/src/Data/RDF.hs
+++ b/src/Data/RDF.hs
@@ -11,6 +11,7 @@
   module Data.RDF.IRI,
   module Data.RDF.Types,
   module Data.RDF.Query,
+  module Data.RDF.Namespace,
 
   -- * RDF type class instances
   module Data.RDF.Graph.TList,
diff --git a/src/Data/RDF/Namespace.hs b/src/Data/RDF/Namespace.hs
--- a/src/Data/RDF/Namespace.hs
+++ b/src/Data/RDF/Namespace.hs
@@ -11,7 +11,7 @@
   mkUri,
   prefixOf, uriOf,
   -- * Predefined namespace values
-  rdf, rdfs, dc, dct, owl, xml, xsd, skos, foaf, ex, ex2,
+  rdf, rdfs, dc, dct, owl, schema, xml, xsd, skos, foaf, ex, ex2,
   standard_ns_mappings, ns_mappings
 ) where
 
@@ -29,7 +29,7 @@
 #endif
 
 standard_namespaces :: [Namespace]
-standard_namespaces = [rdf, rdfs, dc, dct, owl, xsd, skos, foaf, ex, ex2]
+standard_namespaces = [rdf, rdfs, dc, dct, schema, owl, xsd, skos, foaf, ex, ex2]
 
 -- |The set of common predefined namespaces as a 'PrefixMappings' value.
 standard_ns_mappings :: PrefixMappings
@@ -62,6 +62,10 @@
 -- |The OWL namespace.
 owl :: Namespace
 owl = mkPrefixedNS' "owl" "http://www.w3.org/2002/07/owl#"
+
+-- |The Schema.org namespace
+schema :: Namespace
+schema = mkPrefixedNS' "schema" "http://schema.org/"
 
 -- |The XML Schema namespace.
 xml :: Namespace
diff --git a/src/Data/RDF/Query.hs b/src/Data/RDF/Query.hs
--- a/src/Data/RDF/Query.hs
+++ b/src/Data/RDF/Query.hs
@@ -18,7 +18,6 @@
 
     -- * RDF graph functions
     isIsomorphic,
-    isGraphIsomorphic,
     expandTriples,
     fromEither,
 
@@ -31,16 +30,12 @@
     absolutizeTriple,
     absolutizeNode,
     absolutizeNodeUnsafe,
-    QueryException(..)
+    QueryException (..),
   )
 where
 
 import Control.Applicative ((<|>))
 import Control.Exception
-import Data.Graph (Graph, graphFromEdges)
-import qualified Data.Graph.Automorphism as Automorphism
-import qualified Data.HashMap.Strict as HashMap
-import Data.HashMap.Strict (HashMap)
 import Data.List
 import Data.Maybe (fromMaybe)
 import Data.RDF.IRI
@@ -136,7 +131,7 @@
     compareNodeUnlessBlank (LNode l1) (LNode l2) = l1 == l2
     compareNodeUnlessBlank (BNodeGen _) (BNode _) = True
     compareNodeUnlessBlank (BNode _) (BNodeGen _) = True
-    compareNodeUnlessBlank _ _ = False
+    compareNodeUnlessBlank _ _ = False -- isn't this exhaustive already?
     compareTripleUnlessBlank :: Triple -> Triple -> Bool
     compareTripleUnlessBlank (Triple s1 p1 o1) (Triple s2 p2 o2) =
       compareNodeUnlessBlank s1 s2
@@ -144,27 +139,6 @@
         && compareNodeUnlessBlank o1 o2
     normalize :: (Rdf a) => RDF a -> Triples
     normalize = sort . nub . expandTriples
-
--- | Compares the structure of two graphs and returns 'True' if their
---   graph structures are identical. This does not consider the nature
---   of each node in the graph, i.e. the URI text of 'UNode' nodes,
---   the generated index of a blank node, or the values in literal
---   nodes. Unsafe because it assumes IRI resolution will succeed, may
---   throw an 'IRIResolutionException` exception.
-isGraphIsomorphic :: (Rdf a, Rdf b) => RDF a -> RDF b -> Bool
-isGraphIsomorphic g1 g2 = Automorphism.isIsomorphic g1' g2'
-  where
-    g1' = rdfGraphToDataGraph g1
-    g2' = rdfGraphToDataGraph g2
-    rdfGraphToDataGraph :: Rdf c => RDF c -> Graph
-    rdfGraphToDataGraph g = dataGraph
-      where
-        triples = expandTriples g
-        triplesHashMap :: HashMap (Subject, Predicate) [Object]
-        triplesHashMap = HashMap.fromListWith (<>) [((s, p), [o]) | Triple s p o <- triples]
-        triplesGrouped :: [((Subject, Predicate), [Object])]
-        triplesGrouped = HashMap.toList triplesHashMap
-        (dataGraph, _, _) = (graphFromEdges . fmap (\((s, p), os) -> (s, p, os))) triplesGrouped
 
 -- | Expand the triples in a graph with the prefix map and base URL
 -- for that graph. Unsafe because it assumes IRI resolution will
diff --git a/src/Rdf4hParseMain.hs b/src/Rdf4hParseMain.hs
--- a/src/Rdf4hParseMain.hs
+++ b/src/Rdf4hParseMain.hs
@@ -75,47 +75,53 @@
         docUri = Just $ T.pack inputUri
         emptyPms = PrefixMappings Map.empty
     case (inputFormat, isUri $ T.pack inputUri) of
-      ("turtle", True) -> parseURL
-        (TurtleParser mInputUri docUri)
-        inputUri
-        >>= \(res :: Either ParseFailure (RDF TList)) ->
-          write outputFormat docUri emptyPms res
-      ("turtle", False) -> ( if inputUri /= "-"
-                               then parseFile (TurtleParser mInputUri docUri) inputUri
-                               else parseString (TurtleParser mInputUri docUri) <$> TIO.getContents
-                           )
-        >>= \(res :: Either ParseFailure (RDF TList)) ->
-          write outputFormat docUri emptyPms res
-      ("ntriples", True) -> parseURL NTriplesParser inputUri
-        >>= \(res :: Either ParseFailure (RDF TList)) ->
-          write outputFormat Nothing emptyPms res
-      ("ntriples", False) -> ( if inputUri /= "-"
-                                 then parseFile NTriplesParser inputUri
-                                 else parseString NTriplesParser <$> TIO.getContents
-                             )
-        >>= \(res :: Either ParseFailure (RDF TList)) ->
-          write outputFormat Nothing emptyPms res
-      ("xml", True) -> parseURL
-        (XmlParser mInputUri docUri)
-        inputUri
-        >>= \(res :: Either ParseFailure (RDF TList)) ->
-          write outputFormat docUri emptyPms res
-      ("xml", False) -> ( if inputUri /= "-"
-                            then parseFile (XmlParser mInputUri docUri) inputUri
-                            else parseString (XmlParser mInputUri docUri) <$> TIO.getContents
-                        )
-        >>= \(res :: Either ParseFailure (RDF TList)) ->
-          write outputFormat docUri emptyPms res
+      ("turtle", True) ->
+        parseURL
+          (TurtleParser mInputUri docUri)
+          inputUri
+          >>= \(res :: Either ParseFailure (RDF TList)) ->
+            write outputFormat docUri emptyPms res
+      ("turtle", False) ->
+        ( if inputUri /= "-"
+            then parseFile (TurtleParser mInputUri docUri) inputUri
+            else parseString (TurtleParser mInputUri docUri) <$> TIO.getContents
+        )
+          >>= \(res :: Either ParseFailure (RDF TList)) ->
+            write outputFormat docUri emptyPms res
+      ("ntriples", True) ->
+        parseURL NTriplesParser inputUri
+          >>= \(res :: Either ParseFailure (RDF TList)) ->
+            write outputFormat Nothing emptyPms res
+      ("ntriples", False) ->
+        ( if inputUri /= "-"
+            then parseFile NTriplesParser inputUri
+            else parseString NTriplesParser <$> TIO.getContents
+        )
+          >>= \(res :: Either ParseFailure (RDF TList)) ->
+            write outputFormat Nothing emptyPms res
+      ("xml", True) ->
+        parseURL
+          (XmlParser mInputUri docUri)
+          inputUri
+          >>= \(res :: Either ParseFailure (RDF TList)) ->
+            write outputFormat docUri emptyPms res
+      ("xml", False) ->
+        ( if inputUri /= "-"
+            then parseFile (XmlParser mInputUri docUri) inputUri
+            else parseString (XmlParser mInputUri docUri) <$> TIO.getContents
+        )
+          >>= \(res :: Either ParseFailure (RDF TList)) ->
+            write outputFormat docUri emptyPms res
       (str, _) -> putStrLn ("Invalid format: " <> str) >> exitFailure
 
 write :: (Rdf a) => String -> Maybe T.Text -> PrefixMappings -> Either ParseFailure (RDF a) -> IO ()
 write format docUri pms res = case res of
   (Left (ParseFailure msg)) -> putStrLn msg >> exitWith (ExitFailure 1)
-  (Right rdf) -> doWriteRdf rdf
+  (Right rdfG) -> doWriteRdf rdfG
   where
-    doWriteRdf rdf = case format of
-      "turtle" -> writeRdf (TurtleSerializer docUri pms) rdf
-      "ntriples" -> writeRdf NTriplesSerializer rdf
+    doWriteRdf rdfG = case format of
+      "turtle" -> writeRdf (TurtleSerializer docUri pms) rdfG
+      "ntriples" -> writeRdf NTriplesSerializer rdfG
       unknown -> error $ "Unknown output format: " <> unknown
 
 -- Get the input base URI from the argument list or flags, using the
diff --git a/src/Text/RDF/RDF4H/XmlParser.hs b/src/Text/RDF/RDF4H/XmlParser.hs
--- a/src/Text/RDF/RDF4H/XmlParser.hs
+++ b/src/Text/RDF/RDF4H/XmlParser.hs
@@ -1,33 +1,24 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE DoAndIfThenElse #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE OverloadedLists     #-}
-{-# LANGUAGE OverloadedStrings   #-}
-{-# LANGUAGE DoAndIfThenElse     #-}
-{-# LANGUAGE RankNTypes          #-}
-{-# LANGUAGE FlexibleContexts    #-}
-{-# LANGUAGE LambdaCase          #-}
 
--- |An parser for the RDF/XML format
--- <http://www.w3.org/TR/REC-rdf-syntax/>.
-
+-- | An parser for the RDF/XML format
+--  <http://www.w3.org/TR/REC-rdf-syntax/>.
 module Text.RDF.RDF4H.XmlParser
-  ( XmlParser(..)
-  , parseXmlDebug
-  ) where
-
-import           Data.RDF.Types hiding (empty, resolveQName)
-import qualified Data.RDF.Types as RDF
-import           Data.RDF.IRI
-import           Data.RDF.Graph.TList
-import           Text.RDF.RDF4H.ParserUtils hiding (Parser)
-import           Text.RDF.RDF4H.XmlParser.Identifiers
-import           Text.RDF.RDF4H.XmlParser.Xmlbf hiding (Node)
-import qualified Text.RDF.RDF4H.XmlParser.Xeno as Xeno
+  ( XmlParser (..),
+    parseXmlDebug,
+  )
+where
 
-import           Control.Applicative
-import           Control.Monad
-import           Control.Monad.Except
-import           Control.Monad.State.Strict
+import Control.Applicative
+import Control.Monad
+import Control.Monad.Except
+import Control.Monad.State.Strict
 #if MIN_VERSION_base(4,9,0)
 #if !MIN_VERSION_base(4,11,0)
 import Data.Semigroup
@@ -35,90 +26,107 @@
 #endif
 #else
 #endif
-import           Data.Set (Set)
-import qualified Data.Set as S
-import qualified Data.Map as Map
-import           Data.Maybe
+
 #if MIN_VERSION_base(4,10,0)
 import           Data.Either
 #else
 #endif
-import           Data.Bifunctor
-import           Data.HashSet (HashSet)
-import qualified Data.HashSet as HS
-import           Data.HashMap.Strict (HashMap)
+import Data.Bifunctor
+import qualified Data.ByteString.Builder as BB
+import qualified Data.ByteString.Lazy as BL
+import Data.HashMap.Strict (HashMap)
 import qualified Data.HashMap.Strict as HM
-import           Data.Text (Text)
+import Data.HashSet (HashSet)
+import qualified Data.HashSet as HS
+import qualified Data.Map as Map
+import Data.Maybe
+import Data.RDF.Graph.TList
+import Data.RDF.IRI
+import Data.RDF.Types hiding (empty, resolveQName)
+import qualified Data.RDF.Types as RDF
+import Data.Set (Set)
+import qualified Data.Set as S
+import Data.Text (Text)
 import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
 import qualified Data.Text.IO as TIO
 import qualified Data.Text.Lazy as TL
-import qualified Data.Text.Encoding as T
-import qualified Data.ByteString.Lazy as BL
-import qualified Data.ByteString.Builder as BB
+import Text.RDF.RDF4H.ParserUtils hiding (Parser)
+import Text.RDF.RDF4H.XmlParser.Identifiers
+import qualified Text.RDF.RDF4H.XmlParser.Xeno as Xeno
+import Text.RDF.RDF4H.XmlParser.Xmlbf hiding (Node)
+
 -- import           Xmlbf hiding (Node, State)
 -- import qualified Xmlbf.Xeno as Xeno
 
 instance RdfParser XmlParser where
   parseString (XmlParser bUrl dUrl) = parseXmlRDF bUrl dUrl
-  parseFile   (XmlParser bUrl dUrl) = parseFile'  bUrl dUrl
-  parseURL    (XmlParser bUrl dUrl) = parseURL'   bUrl dUrl
+  parseFile (XmlParser bUrl dUrl) = parseFile' bUrl dUrl
+  parseURL (XmlParser bUrl dUrl) = parseURL' bUrl dUrl
 
--- |Configuration for the XML parser
-data XmlParser =
-  XmlParser (Maybe BaseUrl) -- ^ The /default/ base URI to parse the document.
-            (Maybe Text) -- ^ The /retrieval URI/ of the XML document.
+-- | Configuration for the XML parser
+data XmlParser
+  = XmlParser
+      (Maybe BaseUrl)
+      -- ^ The /default/ base URI to parse the document.
+      (Maybe Text)
+      -- ^ The /retrieval URI/ of the XML document.
 
-parseFile' :: (Rdf a)
-  => Maybe BaseUrl
-  -> Maybe Text
-  -> FilePath
-  -> IO (Either ParseFailure (RDF a))
+parseFile' ::
+  (Rdf a) =>
+  Maybe BaseUrl ->
+  Maybe Text ->
+  FilePath ->
+  IO (Either ParseFailure (RDF a))
 parseFile' bUrl dUrl fpath = parseXmlRDF bUrl dUrl <$> TIO.readFile fpath
 
-parseURL' :: (Rdf a)
-  => Maybe BaseUrl
-  -- ^ The optional base URI of the document.
-  -> Maybe Text
-  -- ^ The document URI (i.e., the URI of the document itself); if Nothing, use location URI.
-  -> String
-  -- ^ The location URI from which to retrieve the XML document.
-  -> IO (Either ParseFailure (RDF a))
-  -- ^ The parse result, which is either a @ParseFailure@ or the RDF
+parseURL' ::
+  (Rdf a) =>
+  -- | The optional base URI of the document.
+  Maybe BaseUrl ->
+  -- | The document URI (i.e., the URI of the document itself); if Nothing, use location URI.
+  Maybe Text ->
+  -- | The location URI from which to retrieve the XML document.
+  String ->
+  -- | The parse result, which is either a @ParseFailure@ or the RDF
   --   corresponding to the XML document.
+  IO (Either ParseFailure (RDF a))
 parseURL' bUrl docUrl = parseFromURL (parseXmlRDF bUrl docUrl)
 
--- |The parser monad.
+-- | The parser monad.
 type Parser = ParserT (ExceptT String (State ParseState))
 
--- |Local state for the parser (dependant on the parent xml elements)
+-- | Local state for the parser (dependant on the parent xml elements)
 data ParseState = ParseState
-  { stateBaseUri :: Maybe BaseUrl
-  -- ^ The local base URI.
-  , stateIdSet :: Set Text
-  -- ^ The set of @rdf:ID@ found in the scope of the current base URI.
-  , statePrefixMapping :: PrefixMappings
-  -- ^ The namespace mapping.
-  , stateLang :: Maybe Text
-  -- ^ The local @xml:lang@
-  , stateNodeAttrs :: HashMap Text Text
-  -- ^ Current node RDF attributes.
-  , stateSubject :: Maybe Subject
-  -- ^ Current subject for triple construction.
-  , stateCollectionIndex :: Int
-  -- ^ Current collection index.
-  , stateGenId :: Int
-  } deriving(Show)
+  { -- | The local base URI.
+    stateBaseUri :: Maybe BaseUrl,
+    -- | The set of @rdf:ID@ found in the scope of the current base URI.
+    stateIdSet :: Set Text,
+    -- | The namespace mapping.
+    statePrefixMapping :: PrefixMappings,
+    -- | The local @xml:lang@
+    stateLang :: Maybe Text,
+    -- | Current node RDF attributes.
+    stateNodeAttrs :: HashMap Text Text,
+    -- | Current subject for triple construction.
+    stateSubject :: Maybe Subject,
+    -- | Current collection index.
+    stateCollectionIndex :: Int,
+    stateGenId :: Int
+  }
+  deriving (Show)
 
--- |Parse a xml Text to an RDF representation
-parseXmlRDF :: (Rdf a)
-  => Maybe BaseUrl
-  -- ^ The base URI for the RDF if required
-  -> Maybe Text
-  -- ^ The request URI for the document to  if available
-  -> Text
-  -- ^ The contents to parse
-  -> Either ParseFailure (RDF a)
-  -- ^ The RDF representation of the triples or ParseFailure
+-- | Parse a xml Text to an RDF representation
+parseXmlRDF ::
+  (Rdf a) =>
+  -- | The base URI for the RDF if required
+  Maybe BaseUrl ->
+  -- | The request URI for the document to  if available
+  Maybe Text ->
+  -- | The contents to parse
+  Text ->
+  -- | The RDF representation of the triples or ParseFailure
+  Either ParseFailure (RDF a)
 parseXmlRDF bUrl dUrl = parseRdf . parseXml
   where
     bUrl' = BaseUrl <$> dUrl <|> bUrl
@@ -127,14 +135,14 @@
     parseRdf' ns = join $ evalState (runExceptT (parseM rdfParser ns)) initState
     initState = ParseState bUrl' mempty mempty empty mempty empty 0 0
 
--- |A parser for debugging purposes.
-parseXmlDebug
-  :: FilePath
-  -- ^ Path of the file to parse.
-  -> IO (RDF TList)
+-- | A parser for debugging purposes.
+parseXmlDebug ::
+  -- | Path of the file to parse.
+  FilePath ->
+  IO (RDF TList)
 parseXmlDebug f = fromRight RDF.empty <$> parseFile (XmlParser (Just . BaseUrl $ "http://base-url.com/") (Just "http://doc-url.com/")) f
 
--- |Document parser
+-- | Document parser
 rdfParser :: Rdf a => Parser (RDF a)
 rdfParser = do
   bUri <- currentBaseUri
@@ -142,8 +150,8 @@
   pEndOfInput
   mkRdf triples bUri <$> currentPrefixMappings
 
--- |Parser for @rdf:RDF@, if present.
--- See: https://www.w3.org/TR/rdf-syntax-grammar/#RDF
+-- | Parser for @rdf:RDF@, if present.
+--  See: https://www.w3.org/TR/rdf-syntax-grammar/#RDF
 pRdf :: Parser Triples
 pRdf = pAnyElement $ do
   attrs <- pRDFAttrs
@@ -152,18 +160,18 @@
   unless (null attrs) $ throwError "rdf:RDF: The set of attributes should be empty."
   pNodeElementList
 
--- |Parser for XML QName: resolve the namespace with the mapping in context.
+-- | Parser for XML QName: resolve the namespace with the mapping in context.
 --
---  Throws an error if the namespace is not defined.
+--   Throws an error if the namespace is not defined.
 pQName :: Text -> Parser Text
 pQName qn = do
   pm <- currentPrefixMappings
   let qn' = resolveQName pm qn >>= validateIRI
   either throwError pure qn'
 
--- |Process the attributes of an XML element.
+-- | Process the attributes of an XML element.
 --
---  To be called __once__ per XML element.
+--   To be called __once__ per XML element.
 pRDFAttrs :: Parser (HashMap Text Text)
 pRDFAttrs = do
   -- Language (xml:lang)
@@ -180,53 +188,49 @@
   setNodeAttrs as
   pure as
   where
-    -- |Check if an XML attribute is a namespace definition
-    --  and if so add it to the mapping.
-    mkNameSpace
-      :: Map.Map Text Text
-      -- ^ Current namespace mapping
-      -> Text
-      -- ^ XML attribute to process
-      -> Text
-      -- ^ Value of the attribute
-      -> Map.Map Text Text
+    mkNameSpace ::
+      -- | Current namespace mapping
+      Map.Map Text Text ->
+      -- | XML attribute to process
+      Text ->
+      -- | Value of the attribute
+      Text ->
+      Map.Map Text Text
     mkNameSpace ns qn iri =
       let qn' = parseQName qn
           ns' = f <$> qn' <*> validateIRI iri
-          f (Nothing     , "xmlns") iri' = Map.insert mempty iri' ns
-          f (Just "xmlns", prefix ) iri' = Map.insert prefix iri' ns
-          f _                       _    = ns
-      in either (const ns) id ns'
-    -- |Check if an XML attribute is an RDF attribute
-    --  and if so resolve its URI and keep it.
-    mkRdfAttribute
-      :: PrefixMappings
-      -- ^ Namespace mapping
-      -> Maybe BaseUrl
-      -- ^ Base URI
-      -> HM.HashMap Text Text
-      -- ^ Current set of RDF attributes
-      -> Text
-      -- ^ XML attribute to process
-      -> Text
-      -- ^ Value of the attribute
-      -> HM.HashMap Text Text
+          f (Nothing, "xmlns") iri' = Map.insert mempty iri' ns
+          f (Just "xmlns", prefix) iri' = Map.insert prefix iri' ns
+          f _ _ = ns
+       in either (const ns) id ns'
+    mkRdfAttribute ::
+      -- | Namespace mapping
+      PrefixMappings ->
+      -- | Base URI
+      Maybe BaseUrl ->
+      -- | Current set of RDF attributes
+      HM.HashMap Text Text ->
+      -- | XML attribute to process
+      Text ->
+      -- | Value of the attribute
+      Text ->
+      HM.HashMap Text Text
     mkRdfAttribute pm bUri as qn v =
       let as' = parseQName qn >>= f
           -- [NOTE] Ignore XML reserved names
           f (Nothing, n)
             | T.isPrefixOf "xml" n = Right as
-            | otherwise            = case bUri of
-                Nothing -> Right as -- [FIXME] manage missing base URI
-                Just (BaseUrl bUri') -> (\a -> HM.insert a v as) <$> resolveIRI bUri' n
+            | otherwise = case bUri of
+              Nothing -> Right as -- [FIXME] manage missing base URI
+              Just (BaseUrl bUri') -> (\a -> HM.insert a v as) <$> resolveIRI bUri' n
           f qn'@(Just prefix, _)
             | T.isPrefixOf "xml" prefix = Right as
             | otherwise = (\a -> HM.insert a v as) <$> resolveQName' pm qn'
-      in either (const as) id as'
+       in either (const as) id as'
 
--- |Return the value of the requested RDF attribute using its URI.
+-- | Return the value of the requested RDF attribute using its URI.
 --
---  Fails if the attribute is not defined.
+--   Fails if the attribute is not defined.
 pRDFAttr :: Text -> Parser Text
 pRDFAttr a = do
   as <- currentNodeAttrs
@@ -239,8 +243,8 @@
 pNodeElementList :: Parser Triples
 pNodeElementList = pWs *> (mconcat <$> some (keepState pNodeElement <* pWs))
 
--- |White spaces parser
---  See: https://www.w3.org/TR/rdf-syntax-grammar/#ws
+-- | White spaces parser
+--   See: https://www.w3.org/TR/rdf-syntax-grammar/#ws
 pWs :: Parser ()
 pWs = maybe True (T.all ws . TL.toStrict) <$> optional pText >>= guard
   where
@@ -259,10 +263,10 @@
   ts2 <- keepState pPropertyEltList
   setSubject (Just s)
   let ts = ts1 <> ts2
-  pure $ maybe ts (:ts) mt
+  pure $ maybe ts (: ts) mt
 
--- |Process the following parts of a @nodeElement@: URI, subject and @rdf:type@.
--- See: https://www.w3.org/TR/rdf-syntax-grammar/#nodeElement
+-- | Process the following parts of a @nodeElement@: URI, subject and @rdf:type@.
+--  See: https://www.w3.org/TR/rdf-syntax-grammar/#nodeElement
 pSubject :: Parser (Node, Maybe Triple)
 pSubject = do
   -- Create the subject
@@ -292,15 +296,17 @@
 pPropertyAttrs :: Node -> Parser Triples
 pPropertyAttrs s = do
   attrs <- currentNodeAttrs
-  HM.elems <$> HM.traverseWithKey f attrs
+  -- HM.elems <$> HM.traverseWithKey f attrs
+  HM.elems <$> HM.traverseWithKey f (HM.delete rdfID attrs)
   where
     f attr value
       | not (isPropertyAttrURI attr) = throwError $ "URI not allowed for attribute: " <> T.unpack attr
       | attr == rdfType = pure $ Triple s rdfTypeNode (unode value)
       | otherwise = do
-          lang <- currentLang
-          pure $ let mkLiteral = maybe plainL (flip plainLL) lang
-                 in Triple s (unode attr) (lnode (mkLiteral value))
+        lang <- currentLang
+        pure $
+          let mkLiteral = maybe plainL (flip plainLL) lang
+           in Triple s (unode attr) (lnode (mkLiteral value))
 
 pLang :: Parser (Maybe Text)
 pLang = optional (pAttr "xml:lang")
@@ -310,16 +316,18 @@
 pBase = optional $ do
   uri <- pAttr "xml:base"
   -- Parse and remove fragment
-  BaseUrl <$> either
-    throwError
-    (pure . serializeIRI . removeIRIFragment)
-    (parseIRI uri)
+  BaseUrl
+    <$> either
+      throwError
+      (pure . serializeIRI . removeIRIFragment)
+      (parseIRI uri)
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#propertyEltList
 pPropertyEltList :: Parser Triples
-pPropertyEltList =  pWs
-                 *> resetCollectionIndex
-                 *> fmap mconcat (many (pPropertyElt <* pWs))
+pPropertyEltList =
+  pWs
+    *> resetCollectionIndex
+    *> fmap mconcat (many (pPropertyElt <* pWs))
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#propertyElt
 pPropertyElt :: Parser Triples
@@ -341,7 +349,7 @@
   where
     listExpansion u
       | u == rdfLi = nextCollectionIndex
-      | otherwise  = pure u
+      | otherwise = pure u
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#resourcePropertyElt
 pResourcePropertyElt :: Node -> Parser Triples
@@ -358,7 +366,7 @@
   let mt = flip Triple p <$> s <*> o
   -- Reify the triple
   ts2 <- maybe (pure mempty) (uncurry reifyTriple) (liftA2 (,) mi mt)
-  pure $ maybe (ts1 <> ts2) (:(ts1 <> ts2)) mt
+  pure $ maybe (ts1 <> ts2) (: (ts1 <> ts2)) mt
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#literalPropertyElt
 pLiteralPropertyElt :: Node -> Parser Triples
@@ -377,7 +385,7 @@
       mt = (\s' -> Triple s' p o) <$> s
   -- Reify the triple
   ts <- maybe (pure mempty) (uncurry reifyTriple) (liftA2 (,) mi mt)
-  pure $ maybe ts (:ts) mt
+  pure $ maybe ts (: ts) mt
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#parseTypeLiteralPropertyElt
 pParseTypeLiteralPropertyElt :: Node -> Parser Triples
@@ -393,7 +401,7 @@
       mt = (\s' -> Triple s' p o) <$> s
   -- Reify the triple
   ts <- maybe (pure mempty) (uncurry reifyTriple) (liftA2 (,) mi mt)
-  pure $ maybe ts (:ts) mt
+  pure $ maybe ts (: ts) mt
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#parseTypeResourcePropertyElt
 pParseTypeResourcePropertyElt :: Node -> Parser Triples
@@ -412,7 +420,7 @@
   -- Explore children
   ts2 <- keepCollectionIndex pPropertyEltList
   --setSubject s
-  pure $ maybe (ts1 <> ts2) ((<> ts2) . (:ts1)) mt
+  pure $ maybe (ts1 <> ts2) ((<> ts2) . (: ts1)) mt
 
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#parseTypeCollectionPropertyElt
 pParseTypeCollectionPropertyElt :: Node -> Parser Triples
@@ -430,7 +438,7 @@
         Nothing ->
           -- Empty collection
           let t = Triple s' p rdfNilNode
-          in ([t] <>) <$> maybe (pure mempty) (`reifyTriple` t) mi
+           in ([t] <>) <$> maybe (pure mempty) (`reifyTriple` t) mi
         Just ts1 -> do
           -- Non empty collection
           s'' <- currentSubject
@@ -476,22 +484,41 @@
   case s of
     Nothing -> pure mempty
     Just s' -> do
-      mi <- optional pIdAttr <* removeNodeAttr rdfID
-      o <- pResourceAttr' <|> pNodeIdAttr' <|> newBNode
+      -- mi <- optional pIdAttr -- <* removeNodeAttr rdfID
+      --
+      notNodeIdAndResource
+      o <- pResourceAttr' <|> pNodeIdAttr' <|> rdfIdAttr' <|> newBNode
       let t = Triple s' p o
       -- Reify triple
-      ts1 <- maybe (pure mempty) (`reifyTriple` t) mi
-      ts2 <- pPropertyAttrs o
-      pure (t:ts1 <> ts2)
+      ts1 <- -- maybe (pure mempty) (`reifyTriple` t) mi
+        do
+          x <- optional pIdAttr
+          case x of
+            Nothing -> mempty
+            rdfIdSub -> maybe (pure mempty) (`reifyTriple` t) rdfIdSub
+      -- ts2 <- mempty
+      --
+      -- attrs <- currentNodeAttrs
+      --
+      ts2 <-
+        case o of
+          (LNode _) -> mempty
+          _ -> pPropertyAttrs o
+      --
+      pure (t : ts1 <> ts2)
   where
+    -- pure (t : ts1)
+
     pResourceAttr' = unode <$> pResourceAttr <* removeNodeAttr rdfResource
     pNodeIdAttr' = BNode <$> pNodeIdAttr <* removeNodeAttr rdfNodeID
+    rdfIdAttr' = pure (lnode (plainL "")) <* (void (pIdAttr <* removeNodeAttr rdfID) <|> noNodeAttrs)
 
 checkAllowedAttributes :: HashSet Text -> Parser ()
 checkAllowedAttributes as = do
   attrs <- currentNodeAttrs
   let diffAttrs = HS.difference (HM.keysSet attrs) as
   unless (null diffAttrs) (throwError $ "Attributes not allowed: " <> show diffAttrs)
+
 -- See: https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-xmlliteral,
 --      https://www.w3.org/TR/rdf-syntax-grammar/#literal
 pXMLLiteral :: Parser Text
@@ -512,6 +539,16 @@
   when notUnique (throwError $ "rdf:ID already used in this context: " <> T.unpack i)
   updateIdSet i
 
+-- Cannot have rdf:nodeID and rdf:resource
+notNodeIdAndResource :: Parser ()
+notNodeIdAndResource = do
+  attrs <- currentNodeAttrs
+  guard
+    ( (HM.member rdfNodeID attrs && not (HM.member rdfResource attrs))
+        || (not (HM.member rdfNodeID attrs) && (HM.member rdfResource attrs))
+        || (not (HM.member rdfNodeID attrs) && not (HM.member rdfResource attrs))
+    )
+
 pNodeIdAttr :: Parser Text
 pNodeIdAttr = do
   i <- pRDFAttr rdfNodeID
@@ -529,10 +566,12 @@
 reifyTriple :: Text -> Triple -> Parser Triples
 reifyTriple i (Triple s p' o) = do
   n <- mkUNodeID i
-  pure [ Triple n rdfTypeNode rdfStatementNode
-       , Triple n rdfSubjectNode s
-       , Triple n rdfPredicateNode p'
-       , Triple n rdfObjectNode o ]
+  pure
+    [ Triple n rdfTypeNode rdfStatementNode,
+      Triple n rdfSubjectNode s,
+      Triple n rdfPredicateNode p',
+      Triple n rdfObjectNode o
+    ]
 
 --------------------------------------------------------------------------------
 -- URI checks
@@ -541,40 +580,43 @@
 checkIRI msg iri = do
   bUri <- maybe mempty unBaseUrl <$> currentBaseUri
   case uriValidate iri of
-    Nothing   -> throwError $ mconcat ["Malformed IRI for \"", msg, "\": ", T.unpack iri]
+    Nothing -> throwError $ mconcat ["Malformed IRI for \"", msg, "\": ", T.unpack iri]
     Just iri' -> either throwError pure (resolveIRI bUri iri')
 
 -- https://www.w3.org/TR/rdf-syntax-grammar/#propertyAttributeURIs
 isPropertyAttrURI :: Text -> Bool
-isPropertyAttrURI uri
-  =  isNotCoreSyntaxTerm uri
-  && uri /= rdfDescription
-  && uri /= rdfLi
-  && isNotOldTerm uri
+isPropertyAttrURI uri =
+  isNotCoreSyntaxTerm uri
+    && uri /= rdfDescription
+    && uri /= rdfLi
+    && isNotOldTerm uri
 
 -- https://www.w3.org/TR/rdf-syntax-grammar/#coreSyntaxTerms
 isNotCoreSyntaxTerm :: Text -> Bool
-isNotCoreSyntaxTerm uri
-  =  uri /= rdfTag && uri /= rdfID && uri /= rdfAbout
-  && uri /= rdfParseType && uri /= rdfResource
-  && uri /= rdfNodeID && uri /= rdfDatatype
+isNotCoreSyntaxTerm uri =
+  uri /= rdfTag && uri /= rdfID && uri /= rdfAbout
+    && uri /= rdfParseType
+    && uri /= rdfResource
+    && uri /= rdfNodeID
+    && uri /= rdfDatatype
 
 -- https://www.w3.org/TR/rdf-syntax-grammar/#oldTerms
 isNotOldTerm :: Text -> Bool
-isNotOldTerm uri =  uri /= rdfAboutEach
-                 && uri /= rdfAboutEachPrefix
-                 && uri /= rdfBagID
+isNotOldTerm uri =
+  uri /= rdfAboutEach
+    && uri /= rdfAboutEachPrefix
+    && uri /= rdfBagID
 
 --------------------------------------------------------------------------------
 -- Parser's state utils
 
--- |Create a new unique blank node
+-- | Create a new unique blank node
 newBNode :: Parser Node
 newBNode = do
-  modify $ \st -> st { stateGenId = stateGenId st + 1 }
+  modify $ \st -> st {stateGenId = stateGenId st + 1}
   BNodeGen . stateGenId <$> get
 
--- |Process a parser, restoring the state except for stateGenId and stateIdSet
+-- | Process a parser, restoring the state except for stateGenId and stateIdSet
 keepState :: Parser a -> Parser a
 keepState p = do
   st <- get
@@ -587,8 +629,8 @@
         is' = stateIdSet st'
     -- Update the set of ID if necessary
     if bUri /= bUri'
-      then put (st { stateGenId = i })
-      else put (st { stateGenId = i, stateIdSet = is <> is' })
+      then put (st {stateGenId = i})
+      else put (st {stateGenId = i, stateIdSet = is <> is'})
 
 currentIdSet :: Parser (Set Text)
 currentIdSet = stateIdSet <$> get
@@ -596,13 +638,18 @@
 updateIdSet :: Text -> Parser ()
 updateIdSet i = do
   is <- currentIdSet
-  modify (\st -> st { stateIdSet = S.insert i is })
+  modify (\st -> st {stateIdSet = S.insert i is})
 
+noNodeAttrs :: Parser ()
+noNodeAttrs = do
+  atts <- currentNodeAttrs
+  guard (HM.null atts)
+
 currentNodeAttrs :: Parser (HashMap Text Text)
 currentNodeAttrs = stateNodeAttrs <$> get
 
 setNodeAttrs :: HashMap Text Text -> Parser ()
-setNodeAttrs as = modify (\st -> st { stateNodeAttrs = as })
+setNodeAttrs as = modify (\st -> st {stateNodeAttrs = as})
 
 removeNodeAttr :: Text -> Parser ()
 removeNodeAttr a = HM.delete a <$> currentNodeAttrs >>= setNodeAttrs
@@ -613,14 +660,14 @@
 updatePrefixMappings :: PrefixMappings -> Parser PrefixMappings
 updatePrefixMappings pm = do
   pm' <- (<> pm) <$> currentPrefixMappings
-  modify (\st -> st { statePrefixMapping = pm' })
+  modify (\st -> st {statePrefixMapping = pm'})
   pure pm'
 
 currentCollectionIndex :: Parser Int
 currentCollectionIndex = stateCollectionIndex <$> get
 
 setCollectionIndex :: Int -> Parser ()
-setCollectionIndex i = modify (\st -> st { stateCollectionIndex = i })
+setCollectionIndex i = modify (\st -> st {stateCollectionIndex = i})
 
 keepCollectionIndex :: Parser a -> Parser a
 keepCollectionIndex p = do
@@ -630,33 +677,34 @@
 -- See: https://www.w3.org/TR/rdf-syntax-grammar/#section-List-Expand
 nextCollectionIndex :: Parser Text
 nextCollectionIndex = do
-  modify $ \st -> st { stateCollectionIndex = stateCollectionIndex st + 1 }
+  modify $ \st -> st {stateCollectionIndex = stateCollectionIndex st + 1}
   (rdfListIndex <>) . T.pack . show . stateCollectionIndex <$> get
 
 resetCollectionIndex :: Parser ()
-resetCollectionIndex = modify $ \st -> st { stateCollectionIndex = 0 }
+resetCollectionIndex = modify $ \st -> st {stateCollectionIndex = 0}
 
 currentBaseUri :: Parser (Maybe BaseUrl)
 currentBaseUri = stateBaseUri <$> get
 
 setBaseUri :: (Maybe BaseUrl) -> Parser ()
-setBaseUri u = modify (\st -> st { stateBaseUri = u })
+setBaseUri u = modify (\st -> st {stateBaseUri = u})
 
 mkUNodeID :: Text -> Parser Node
 mkUNodeID t = mkUnode <$> currentBaseUri
   where
-    mkUnode = unode . \case
-      Nothing          -> t
-      Just (BaseUrl u) -> mconcat [u, "#", t]
+    mkUnode =
+      unode . \case
+        Nothing -> t
+        Just (BaseUrl u) -> mconcat [u, "#", t]
 
 currentSubject :: Parser (Maybe Subject)
 currentSubject = stateSubject <$> get
 
 setSubject :: (Maybe Subject) -> Parser ()
-setSubject s = modify (\st -> st { stateSubject = s })
+setSubject s = modify (\st -> st {stateSubject = s})
 
 currentLang :: Parser (Maybe Text)
 currentLang = stateLang <$> get
 
 setLang :: (Maybe Text) -> Parser ()
-setLang lang = modify (\st -> st { stateLang = lang })
+setLang lang = modify (\st -> st {stateLang = lang})
diff --git a/testsuite/tests/W3C/W3CAssertions.hs b/testsuite/tests/W3C/W3CAssertions.hs
--- a/testsuite/tests/W3C/W3CAssertions.hs
+++ b/testsuite/tests/W3C/W3CAssertions.hs
@@ -1,21 +1,22 @@
 module W3C.W3CAssertions
-  ( runManifestTests
-  , assertIsIsomorphic
-  , assertIsParsed
-  , assertIsNotParsed
-  , nodeURI
-  ) where
+  ( runManifestTests,
+    assertIsIsomorphic,
+    assertIsParsed,
+    assertIsNotParsed,
+    nodeURI,
+  )
+where
 
-import           Data.Semigroup ((<>))
+import Data.RDF
+import Data.Semigroup ((<>))
 import qualified Data.Text as T
-import           Data.RDF
 import qualified Test.HUnit as TU
-import           Test.Tasty
-import           W3C.Manifest
+import Test.Tasty
+import W3C.Manifest
 
 runManifestTests :: (TestEntry -> TestTree) -> Manifest -> TestTree
 runManifestTests mfEntryToTest manifest =
-    testGroup (T.unpack $ description manifest) $ mfEntryToTest <$> entries manifest
+  testGroup (T.unpack $ description manifest) $ mfEntryToTest <$> entries manifest
 
 assertIsIsomorphic :: IO (RDF TList) -> IO (RDF TList) -> IO ()
 assertIsIsomorphic r1 r2 = do
@@ -23,21 +24,23 @@
   gr2 <- r2
   TU.assertBool ("not isomorphic: " <> show gr1 <> " compared with " <> show gr2) (isSame gr1 gr2) -- (isGraphIsomorphic gr1 gr2)
   where
-    noBlankNodes g = (all noBlanks . expandTriples) g
-    noBlanks (Triple s p o) = not (blankNode s)
-                           && not (blankNode p)
-                           && not (blankNode o)
-    blankNode (BNode _)    = True
-    blankNode (BNodeGen _) = True
-    blankNode _            = False
-    isSame g1 g2 =
-      if (noBlankNodes g1) && (noBlankNodes g2)
-      -- can compare URIs and literals as well as structure
-      then isIsomorphic g1 g2
-      -- cannot compare blank nodes with generated blank nodes,
-      -- so instead just compare the RDF graph structure of the
-      -- mf:action parsed RDF with the expected mf:result structure.
-      else isGraphIsomorphic g1 g2
+    -- noBlankNodes g = (all noBlanks . expandTriples) g
+    -- noBlanks (Triple s p o) =
+    --   not (blankNode s)
+    --     && not (blankNode p)
+    --     && not (blankNode o)
+    -- blankNode (BNode _) = True
+    -- blankNode (BNodeGen _) = True
+    -- blankNode _ = False
+    isSame g1 g2 = isIsomorphic g1 g2
+
+-- if (noBlankNodes g1) && (noBlankNodes g2)
+-- -- can compare URIs and literals as well as structure
+-- then isIsomorphic g1 g2
+-- -- cannot compare blank nodes with generated blank nodes,
+-- -- so instead just compare the RDF graph structure of the
+-- -- mf:action parsed RDF with the expected mf:result structure.
+-- else isGraphIsomorphic g1 g2
 
 assertIsParsed :: IO (Either ParseFailure (RDF TList)) -> TU.Assertion
 assertIsParsed r1 = do
