diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,47 @@
 # Revision history for net-spider-rpl
 
+## 0.2.0.0  -- 2019-07-15
+
+* [BREAKING CHANGE] Type class instances from net-spider-pangraph are
+  removed. This is because
+
+    1. `NetSpider.GraphML.Writer` from net-spider can be used to
+       format SnapshotGraphs into GraphML
+    2. net-spider-pangraph has relatively a heavy dependency set
+    3. `NetSpider.Pangraph.ToAttributes` typeclass instance can be
+       easily implemented by `NetSpider.Pangraph.attributesFromGraphML`.
+
+* Add `NetSpider.GraphML.ToAttributes` instance to RPL data types so
+  that SnapshotGraphs can be formatted to GraphML.
+
+### Combined module
+
+* Add `SnapshotGraphCombined` type synonym.
+* `combineNodes` now prefers the input node with the latest timestamp
+  as the basis of the result.
+
+### ContikiNG module
+
+* Bug fix for DIO entries including the infinite rank.
+* Add detailed doc to `parseFile`.
+
+### DAO module
+
+* Add `SnapshotGraphDAO` type synonym.
+* Add `daoDefQuery` function.
+* Fix writer and parser of `dao_route_num` attribute.
+
+### DIO module
+
+* Add `SnapshotGraphDIO` type synonym.
+* Add `dioDefQuery` function.
+
+### FindingID module
+
+* Add `NetSpider.GraphML.Writer.ToNodeID` intance to `FindingID` and
+  `IPv6ID`.
+
+
 ## 0.1.0.0  -- 2019-05-03
 
 * First version. Released on an unsuspecting world.
diff --git a/net-spider-rpl.cabal b/net-spider-rpl.cabal
--- a/net-spider-rpl.cabal
+++ b/net-spider-rpl.cabal
@@ -1,5 +1,5 @@
 name:                   net-spider-rpl
-version:                0.1.0.0
+version:                0.2.0.0
 author:                 Toshio Ito <debug.ito@gmail.com>
 maintainer:             Toshio Ito <debug.ito@gmail.com>
 license:                BSD3
@@ -13,7 +13,8 @@
                         test/data/cooja.log,
                         test/data/syslog_root.log,
                         test/data/syslog_nonroot.log,
-                        test/data/syslog_sr_tables.log
+                        test/data/syslog_sr_tables.log,
+                        test/data/syslog_inf_rank.log
 homepage:               https://github.com/debug-ito/net-spider
 bug-reports:            https://github.com/debug-ito/net-spider/issues
 
@@ -33,8 +34,7 @@
   build-depends:        base >=4.11.1.0 && <4.13,
                         aeson >=1.3.1.1 && <1.5,
                         greskell >=0.2.3.0 && <0.3,
-                        net-spider >=0.3.0.0 && <0.4,
-                        net-spider-pangraph >=0.1.0.0 && <0.2,
+                        net-spider >=0.3.1.0 && <0.4,
                         text >=1.2.3.1 && <1.3,
                         ip >=1.3.0 && <1.5,
                         hashable >=1.2.7.0 && <1.3,
diff --git a/src/NetSpider/RPL.hs b/src/NetSpider/RPL.hs
--- a/src/NetSpider/RPL.hs
+++ b/src/NetSpider/RPL.hs
@@ -6,6 +6,7 @@
 --
 -- This module defines NetSpider data model and utility for RPL
 -- networks.
+-- For usage example, see [an example program](https://github.com/debug-ito/net-spider/tree/master/net-spider-rpl-example/src/NetSpider/RPL/Example.hs).
 --
 -- RPL ( [IPv6 Routing Protocol for Low-Power and Lossy Networks, RFC 6550](https://tools.ietf.org/html/rfc6550) )
 -- is a routing protocol for small wireless devices. Each node
@@ -46,6 +47,7 @@
 -- files generated by Contiki-NG applications.
 -- [Contiki-NG](http://contiki-ng.org/) is a tiny operating system for
 -- wireless devices that supports RPL.
+--
 module NetSpider.RPL
        ( module NetSpider.RPL.FindingID,
          module NetSpider.RPL.DIO,
diff --git a/src/NetSpider/RPL/Combined.hs b/src/NetSpider/RPL/Combined.hs
--- a/src/NetSpider/RPL/Combined.hs
+++ b/src/NetSpider/RPL/Combined.hs
@@ -9,29 +9,31 @@
 -- "NetSpider.RPL.DAO") graphs.
 module NetSpider.RPL.Combined
   ( -- * Functions
+    combineGraphs,
     combineNodes,
     combineLinks,
-    combineGraphs,
     -- * Types
+    SnapshotGraphCombined,
     CombinedNode(..),
     CombinedLink(..),
     combinedLinkType
   ) where
 
 import Data.Bifunctor (bimap, second)
+import Data.List (sortOn, reverse)
 import Data.Semigroup (Semigroup(..))
 import Data.Maybe (isJust)
 import Data.Monoid (Monoid(..), First(..))
 import GHC.Exts (groupWith)
-import qualified NetSpider.Pangraph as Pan
+import qualified NetSpider.GraphML.Writer as GraphML
 import NetSpider.Snapshot
-  ( SnapshotNode, SnapshotLink,
-    nodeId, nodeAttributes
+  ( SnapshotNode, SnapshotLink, SnapshotGraph,
+    nodeId, nodeAttributes, nodeTimestamp
   )
 
 import NetSpider.RPL.FindingID (FindingID(..), FindingType(..), IPv6ID, ipv6Only)
-import NetSpider.RPL.DIO (DIONode, MergedDIOLink)
-import NetSpider.RPL.DAO (DAONode, DAOLink)
+import NetSpider.RPL.DIO (DIONode, MergedDIOLink, SnapshotGraphDIO)
+import NetSpider.RPL.DAO (DAONode, DAOLink, SnapshotGraphDAO)
 
 -- | Node attributes combining 'DIONode' and 'DAONode'.
 data CombinedNode =
@@ -53,25 +55,29 @@
   mappend a b = a <> b
   mempty = CombinedNode Nothing Nothing
 
-instance Pan.ToAttributes CombinedNode where
-  toAttributes cn = (Pan.toAttributes $ attrsDIO cn)
-                    ++ (Pan.toAttributes $ attrsDAO cn)
+instance GraphML.ToAttributes CombinedNode where
+  toAttributes cn = (GraphML.toAttributes $ attrsDIO cn)
+                    ++ (GraphML.toAttributes $ attrsDAO cn)
 
 -- | Link attribute combining 'MergedDIOLink' and 'DAOLink'.
 data CombinedLink = CombinedDIOLink MergedDIOLink
                   | CombinedDAOLink DAOLink
                   deriving (Show,Eq,Ord)
 
-instance Pan.ToAttributes CombinedLink where
+instance GraphML.ToAttributes CombinedLink where
   toAttributes (CombinedDIOLink ll) =
-    ("link_type", "dio") : Pan.toAttributes ll
-  toAttributes (CombinedDAOLink sl) =
-    ("link_type", "dao") : Pan.toAttributes sl
+    ("link_type", GraphML.AttrString "dio") : GraphML.toAttributes ll
+  toAttributes (CombinedDAOLink ll) =
+    ("link_type", GraphML.AttrString "dao") : GraphML.toAttributes ll
 
 combinedLinkType :: CombinedLink -> FindingType
 combinedLinkType (CombinedDIOLink _) = FindingDIO
 combinedLinkType (CombinedDAOLink _) = FindingDAO
 
+-- | Combine DIO and DAO 'SnapshotNode's. Attributes from 'DIONode'
+-- and 'DAONode' for the same 'IPv6ID' are combined into one
+-- 'CombinedNode'. Timestamp of a combined 'SnapshotNode' is the
+-- latest timestamp of input nodes for that 'IPv6ID'.
 combineNodes :: [SnapshotNode FindingID DIONode]
              -> [SnapshotNode FindingID DAONode]
              -> [SnapshotNode IPv6ID CombinedNode]
@@ -79,8 +85,9 @@
   where
     fromDIO = bimap ipv6Only (\ln -> CombinedNode (Just ln) Nothing)
     fromDAO = bimap ipv6Only (\sn -> CombinedNode Nothing (Just sn))
-    concatNodes nodes = map merge $ groupWith nodeId nodes
+    concatNodes nodes = map (merge . sortByTimestamp) $ groupWith nodeId nodes
       where
+        sortByTimestamp = reverse . sortOn nodeTimestamp
         merge [] = error "Empty group should not happen."
         merge group_nodes@(head_node : _) =
           case mmerged_attr of
@@ -95,6 +102,9 @@
             mmerged_attr = mconcat $ map nodeAttributes group_nodes
             hasNodeAttr n = isJust $ nodeAttributes n
 
+-- | Convert DIO and DAO links into combined links. Despite its name,
+-- this function does not combine input links. It just make one
+-- combined link from each of the input links.
 combineLinks :: [SnapshotLink FindingID MergedDIOLink]
              -> [SnapshotLink FindingID DAOLink]
              -> [SnapshotLink IPv6ID CombinedLink]
@@ -103,8 +113,13 @@
     fromDIO = bimap ipv6Only (\ll -> CombinedDIOLink ll)
     fromDAO = bimap ipv6Only (\sl -> CombinedDAOLink sl)
 
-combineGraphs :: ([SnapshotNode FindingID DIONode], [SnapshotLink FindingID MergedDIOLink])
-              -> ([SnapshotNode FindingID DAONode], [SnapshotLink FindingID DAOLink])
-              -> ([SnapshotNode IPv6ID CombinedNode], [SnapshotLink IPv6ID CombinedLink])
+-- | 'SnapshotGraph' combining DIO and DAO networks.
+type SnapshotGraphCombined = SnapshotGraph IPv6ID CombinedNode CombinedLink
+
+-- | Combine DIO and DAO graphs into the combined graph, using
+-- 'combineNodes' and 'combineLinks'.
+combineGraphs :: SnapshotGraphDIO
+              -> SnapshotGraphDAO
+              -> SnapshotGraphCombined
 combineGraphs (dio_ns, dio_ls) (dao_ns, dao_ls) =
   (combineNodes dio_ns dao_ns, combineLinks dio_ls dao_ls)
diff --git a/src/NetSpider/RPL/ContikiNG.hs b/src/NetSpider/RPL/ContikiNG.hs
--- a/src/NetSpider/RPL/ContikiNG.hs
+++ b/src/NetSpider/RPL/ContikiNG.hs
@@ -42,7 +42,7 @@
 import NetSpider.RPL.FindingID (FindingID(FindingID), FindingType(..))
 import NetSpider.RPL.IPv6 (isLinkLocal, setPrefix, getPrefix)
 import qualified NetSpider.RPL.DIO as DIO
-import NetSpider.RPL.DIO (FoundNodeDIO, dioLinkState)
+import NetSpider.RPL.DIO (FoundNodeDIO, dioLinkState, Rank)
 import qualified NetSpider.RPL.DAO as DAO
 import NetSpider.RPL.DAO (FoundNodeDAO)
 
@@ -60,13 +60,31 @@
     extract [] = Nothing
     extract ((a,_) : _) = Just a
 
+runParser' :: Parser a
+           -> String -- ^ error message
+           -> String -- ^ input
+           -> Parser a
+runParser' p err input =
+  case runParser p input of
+    Nothing -> fail err
+    Just a -> return a
+
 -- | Read and parse a log file from a Contiki-NG application to make
 -- 'FoundNodeDIO' and 'FoundNodeDAO'.
 --
+-- Currently this parser function supports logs from \"rpl-lite\"
+-- module only.
+--
 -- It assumes that each line of log file has prefix, and that the
 -- prefix contains timestamp information. You have to pass the parser
 -- for the prefix to this function. For example, if you read a log
 -- file generated by Cooja simulator, use 'pCoojaLogHead'' parser.
+--
+-- One 'FoundNodeDIO' object is parsed from one block of log lines
+-- from the rpl module. On the other hand, one or more 'FoundNodeDAO'
+-- objects are parsed from one block of log lines from the rpl
+-- module. The 'FoundNodeDAO' objects generated from the same log
+-- block share the same timestamp.
 parseFile :: Parser Timestamp -- ^ Parser for log prefix
           -> FilePath -- ^ File to read
           -> IO ([FoundNodeDIO], [FoundNodeDAO])
@@ -152,7 +170,7 @@
         Nothing -> return Nothing
         Just line ->
           case runParser ((Left <$> pEnd) <|> (Right <$> pBody)) line of
-            Nothing -> return Nothing
+            Nothing -> throwIO $ ParseError ("Parse error at line: " <> line)
             Just (Left _) -> return $ Just $ reverse acc
             Just (Right body) -> go (body : acc)
 
@@ -276,13 +294,29 @@
 pExpectChar :: Char -> Parser Bool
 pExpectChar exp_c = fmap (== Just exp_c) $ optional P.get
 
+pNeighborAndRank :: Parser (IPv6, Rank)
+pNeighborAndRank = spaced <|> non_spaced
+  where
+    spaced = do
+      addr <- pMaybeCompactAddress
+      P.skipSpaces
+      rank <- pNum
+      void $ P.string ", "
+      return (addr, rank)
+    non_spaced = do
+      -- Rank is so large that there is no space between the address and rank.
+      -- This case happens when the rank is 5 digits.
+      addr_and_rank <- P.munch isAddressChar
+      void $ P.string ", "
+      let (addr_str, rank_str) = splitAt (length addr_and_rank - 5) addr_and_rank
+      addr <- runParser' pMaybeCompactAddress ("Failed to parse address:" <> addr_str) addr_str
+      rank <- runParser' pNum ("Failed to parser rank:" <> rank_str) rank_str
+      return (addr, rank)
+
 pDIONeighbor :: Parser (IPv6, DIO.DIOLink)
 pDIONeighbor = do
   void $ P.string "nbr: "
-  neighbor_addr <- pMaybeCompactAddress
-  P.skipSpaces
-  neighbor_rank <- pNum
-  void $ P.string ", "
+  (neighbor_addr, neighbor_rank) <- pNeighborAndRank
   P.skipSpaces
   metric <- pNum
   void $ P.string " => "
diff --git a/src/NetSpider/RPL/DAO.hs b/src/NetSpider/RPL/DAO.hs
--- a/src/NetSpider/RPL/DAO.hs
+++ b/src/NetSpider/RPL/DAO.hs
@@ -8,9 +8,11 @@
 module NetSpider.RPL.DAO
   ( -- * Types
     FoundNodeDAO,
+    SnapshotGraphDAO,
     DAONode(..),
     DAOLink(..),
-    -- * Unifier
+    -- * Query
+    daoDefQuery,
     daoUnifierConf
   ) where
 
@@ -21,13 +23,15 @@
 import Control.Applicative ((<$>), (<*>))
 import Data.Aeson (ToJSON(..))
 import Data.Greskell
-  ( parseOneValue
+  ( parseOneValue, parseListValues
   )
 import Data.Greskell.Extra (writePropertyKeyValues)
+import Data.Maybe (listToMaybe)
 import NetSpider.Found (FoundNode)
 import NetSpider.Graph (NodeAttributes(..), LinkAttributes(..))
-import qualified NetSpider.Pangraph as Pan
-import NetSpider.Pangraph.Atom (toAtom, Atom)
+import qualified NetSpider.GraphML.Writer as GraphML
+import qualified NetSpider.Query as Query
+import NetSpider.Snapshot (SnapshotGraph)
 import NetSpider.Unify (UnifyStdConfig, lsLinkAttributes, latestLinkSample)
 import qualified NetSpider.Unify as Unify
 
@@ -36,6 +40,10 @@
 -- | 'FoundNode' for a network described by DAOs.
 type FoundNodeDAO = FoundNode FindingID DAONode DAOLink
 
+-- | 'SnapshotGraph' for a network described by DAOs. This is what you
+-- get by 'daoDefQuery'.
+type SnapshotGraphDAO = SnapshotGraph FindingID DAONode DAOLink
+
 -- | Node attributes about DAO.
 data DAONode =
   DAONode
@@ -53,16 +61,19 @@
 instance NodeAttributes DAONode where
   writeNodeAttributes dn = writePropertyKeyValues pairs
     where
-      pairs = [ ("dao_route_num", toJSON $ daoRouteNum dn)
-              ]
-  parseNodeAttributes ps = DAONode <$> parseOneValue "dao_route_num" ps
+      pairs =
+        case daoRouteNum dn of
+          Nothing -> []
+          Just n -> [("dao_route_num", toJSON n)]
+  parseNodeAttributes ps = fmap (DAONode . listToMaybe) $ parseListValues "dao_route_num" ps
 
-instance Pan.ToAttributes DAONode where
-  toAttributes dn = 
+instance GraphML.ToAttributes DAONode where
+  toAttributes dn =
     case daoRouteNum dn of
       Nothing -> []
-      Just p -> [("dao_route_num", toAtom $ p)]
+      Just p -> [("dao_route_num", GraphML.AttrInt $ fromIntegral $ p)]
 
+
 -- | Link attributes about DAO.
 --
 -- In Storing mode of RPL, a 'DAOLink' represents a link between the
@@ -87,9 +98,18 @@
               ]
   parseLinkAttributes ps = DAOLink <$> parseOneValue "path_lifetime_sec" ps
 
-instance Pan.ToAttributes DAOLink where
-  toAttributes dl = [ ("path_lifetime_sec", toAtom $ pathLifetimeSec dl) ]
+instance GraphML.ToAttributes DAOLink where
+  toAttributes dl = [ ("path_lifetime_sec", GraphML.AttrInt $ fromIntegral $ pathLifetimeSec dl) ]
 
--- | 'UnifyStdConfig' for RPL DAO data.
+-- | Default 'Query.Query' for DAO nodes.
+daoDefQuery :: [FindingID] -- ^ 'Query.startsFrom' field.
+            -> Query.Query FindingID DAONode DAOLink DAOLink
+daoDefQuery start =
+  (Query.defQuery start)
+  { Query.startsFrom = start,
+    Query.unifyLinkSamples = Unify.unifyStd daoUnifierConf
+  }
+
+-- | 'UnifyStdConfig' for RPL DAO data. Used in 'defQuery'.
 daoUnifierConf :: UnifyStdConfig FindingID DAONode DAOLink DAOLink ()
 daoUnifierConf = Unify.defUnifyStdConfig { Unify.negatesLinkSample = \_ _ -> False }
diff --git a/src/NetSpider/RPL/DIO.hs b/src/NetSpider/RPL/DIO.hs
--- a/src/NetSpider/RPL/DIO.hs
+++ b/src/NetSpider/RPL/DIO.hs
@@ -8,6 +8,7 @@
 module NetSpider.RPL.DIO
   ( -- * Types
     FoundNodeDIO,
+    SnapshotGraphDIO,
     DIONode(..),
     DIOLink(..),
     dioLinkState,
@@ -16,7 +17,8 @@
     NeighborType(..),
     neighborTypeToText,
     neighborTypeFromText,
-    -- * Unifier
+    -- * Query
+    dioDefQuery,
     dioUnifierConf
   ) where
 
@@ -33,8 +35,9 @@
 import Data.Word (Word32)
 import NetSpider.Found (FoundNode, LinkState(..))
 import NetSpider.Graph (NodeAttributes(..), LinkAttributes(..))
-import qualified NetSpider.Pangraph as Pan
-import NetSpider.Pangraph.Atom (toAtom, Atom)
+import qualified NetSpider.GraphML.Writer as GraphML
+import qualified NetSpider.Query as Query
+import NetSpider.Snapshot (SnapshotGraph)
 import NetSpider.Unify (UnifyStdConfig, lsLinkAttributes, latestLinkSample)
 import qualified NetSpider.Unify as Unify
 
@@ -43,6 +46,10 @@
 -- | 'FoundNode' for a network described by DIOs.
 type FoundNodeDIO = FoundNode FindingID DIONode DIOLink
 
+-- | 'SnapshotGraph' for a network described by DIOs. This is what you
+-- get by 'dioDefQuery'.
+type SnapshotGraphDIO = SnapshotGraph FindingID DIONode MergedDIOLink
+
 -- | RPL rank
 type Rank = Word
 
@@ -70,9 +77,9 @@
                            <$> parseOneValue "rank" ps
                            <*> parseOneValue "dio_interval" ps
 
-instance Pan.ToAttributes DIONode where
-  toAttributes ln = [ ("rank", toAtom $ rank ln),
-                      ("dio_interval", toAtom $ dioInterval ln)
+instance GraphML.ToAttributes DIONode where
+  toAttributes ln = [ ("rank", GraphML.AttrInt $ fromIntegral $ rank ln),
+                      ("dio_interval", GraphML.AttrInt $ fromIntegral $ dioInterval ln)
                     ]
 
 -- | Classification of RPL neighbors.
@@ -147,19 +154,17 @@
     PreferredParent -> LinkToTarget
     _ -> LinkUnused
 
-toAttributesPrefix :: Atom -> DIOLink -> [Pan.Attribute]
-toAttributesPrefix prefix ll = 
-  [ (prefix <> "neighbor_type", toAtom $ neighborTypeToText $ neighborType ll),
-    (prefix <> "neighbor_rank", toAtom $ neighborRank ll)
-  ]
-  ++
-  ( case metric ll of
-      Nothing -> []
-      Just metric_val -> [(prefix <> "metric", toAtom metric_val)]
-  )
-
-instance Pan.ToAttributes DIOLink where
-  toAttributes = toAttributesPrefix ""
+instance GraphML.ToAttributes DIOLink where
+  toAttributes ll = [ ("neighbor_type", GraphML.AttrString $ neighborTypeToText $ neighborType ll),
+                      ("neighbor_rank", GraphML.AttrInt $ fromIntegral $ neighborRank ll)
+                    ]
+                    ++ at_metric
+    where
+      at_metric =
+        case metric ll of
+          Nothing -> []
+          Just m -> [("metric", GraphML.AttrInt $ fromIntegral m)]
+                    
 
 -- | Link attributes merging two 'DIOLink's from the two end nodes
 -- of the link.
@@ -170,7 +175,24 @@
   }
   deriving (Show,Eq,Ord)
 
--- | 'UnifyStdConfig' for RPL DIO data
+withKeyPrefix :: Monoid k
+              => k
+              -> [(k, v)]
+              -> [(k, v)]
+withKeyPrefix prefix = map prependPrefix
+  where
+    prependPrefix (k, v) = (prefix <> k, v)
+
+-- | Default 'Query.Query' for DIO nodes.
+dioDefQuery :: [FindingID] -- ^ 'Query.startsFrom' field.
+         -> Query.Query FindingID DIONode DIOLink MergedDIOLink
+dioDefQuery start =
+  (Query.defQuery start)
+  { Query.startsFrom = start,
+    Query.unifyLinkSamples = Unify.unifyStd dioUnifierConf
+  }
+
+-- | 'UnifyStdConfig' for RPL DIO data. Used in 'defQuery'.
 dioUnifierConf :: UnifyStdConfig FindingID DIONode DIOLink MergedDIOLink ()
 dioUnifierConf = Unify.UnifyStdConfig
                  { Unify.makeLinkSubId = const (),
@@ -197,11 +219,11 @@
       where
         main_ll = lsLinkAttributes main_link
 
-instance Pan.ToAttributes MergedDIOLink where
+instance GraphML.ToAttributes MergedDIOLink where
   toAttributes ml =
-    toAttributesPrefix "source_" (fromSource ml)
+    (withKeyPrefix "source_" $ GraphML.toAttributes $ fromSource ml)
     ++
     ( case fromDest ml of
         Nothing -> []
-        Just dl -> toAttributesPrefix "dest_" dl
+        Just dl -> withKeyPrefix "dest_" $ GraphML.toAttributes dl
     )
diff --git a/src/NetSpider/RPL/FindingID.hs b/src/NetSpider/RPL/FindingID.hs
--- a/src/NetSpider/RPL/FindingID.hs
+++ b/src/NetSpider/RPL/FindingID.hs
@@ -33,7 +33,7 @@
 import GHC.Generics (Generic)
 import Net.IPv6 (IPv6(..))
 import qualified Net.IPv6 as IPv6
-import NetSpider.Pangraph.Atom (ToAtom(..))
+import NetSpider.GraphML.Writer (ToNodeID(..))
 
 -- | Type of local finding.
 data FindingType = FindingDIO
@@ -107,8 +107,8 @@
       ft = findingType fid
       addr_id = IPv6ID $ nodeAddress fid
 
-instance ToAtom FindingID where
-  toAtom = toAtom . idToText
+instance ToNodeID FindingID where
+  toNodeID = idToText
 
 -- | 'IPv6' address with additional type-class instances.
 newtype IPv6ID = IPv6ID { unIPv6ID :: IPv6 }
@@ -136,8 +136,8 @@
 instance FromGraphSON IPv6ID where
   parseGraphSON gv = parseIPv6IDFromText =<< parseGraphSON gv
 
-instance ToAtom IPv6ID where
-  toAtom = toAtom . ipv6ToText
+instance ToNodeID IPv6ID where
+  toNodeID = ipv6ToText
 
 -- | Extract 'IPv6ID' from 'FindingID'.
 ipv6Only :: FindingID -> IPv6ID
diff --git a/test/NetSpider/RPL/ContikiNGSpec.hs b/test/NetSpider/RPL/ContikiNGSpec.hs
--- a/test/NetSpider/RPL/ContikiNGSpec.hs
+++ b/test/NetSpider/RPL/ContikiNGSpec.hs
@@ -256,4 +256,41 @@
       (got_dios, got_daos) <- parseFile pHead "test/data/syslog_sr_tables.log"
       got_dios `shouldBe` [exp_dio1, exp_dio2]
       got_daos `shouldMatchList` exp_daos
-
+    specify "syslog_inf_rank" $ do
+      let exp_dio =
+            FoundNode
+            { subjectNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:bcc:1008]",
+              foundAt = fromEpochMillisecond 1551104666000,
+              nodeAttributes = DIO.DIONode { DIO.rank = 65535, DIO.dioInterval = 12 },
+              neighborLinks = exp_links
+            }
+          exp_links =
+            [ FoundLink
+              { targetNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:bcc:100a]",
+                linkState = LinkUnused,
+                linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
+                                               DIO.neighborRank = 492,
+                                               DIO.metric = Just 601
+                                             }
+              },
+              FoundLink
+              { targetNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:9221:d51a]",
+                linkState = LinkUnused,
+                linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
+                                               DIO.neighborRank = 422,
+                                               DIO.metric = Just 601
+                                             }
+              },
+              FoundLink
+              { targetNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:bcc:d5e8]",
+                linkState = LinkUnused,
+                linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
+                                               DIO.neighborRank = 65535,
+                                               DIO.metric = Just 133
+                                             }
+              }
+            ]
+          pHead = pSyslogHead 2019 Nothing
+      (got_dios, got_daos) <- parseFile pHead "test/data/syslog_inf_rank.log"
+      got_dios `shouldBe` [exp_dio]
+      got_daos `shouldMatchList` []
diff --git a/test/data/syslog_inf_rank.log b/test/data/syslog_inf_rank.log
new file mode 100644
--- /dev/null
+++ b/test/data/syslog_inf_rank.log
@@ -0,0 +1,6 @@
+Feb 25 14:24:26 host-name tag[14621]: [WARN: RPL       ] no parent, scheduling periodic DIS, will leave if no parent is found
+Feb 25 14:24:26 host-name tag[14621]: [INFO: RPL       ] nbr: own state, addr fd00::aaa:bbbb:bcc:1008, DAG state: initialized, MOP 1 OCP 1 rank 65535 max-rank 2041, dioint 12, nbr count 3 (Parent switch)
+Feb 25 14:24:26 host-name tag[14621]: [INFO: RPL       ] nbr: fe80::aaa:bbbb:bcc:100a  492,   601 =>  1093 --  8    f  (last tx 0 min ago, better since 0 min)
+Feb 25 14:24:26 host-name tag[14621]: [INFO: RPL       ] nbr: fe80::aaa:bbbb:9221:d51a  422,   601 =>  1023 --  8    f  (last tx 0 min ago, better since 0 min)
+Feb 25 14:24:26 host-name tag[14621]: [INFO: RPL       ] nbr: fe80::aaa:bbbb:bcc:d5e865535,   133 => 65535 --  5    f  (last tx 0 min ago)
+Feb 25 14:24:26 host-name tag[14621]: [INFO: RPL       ] nbr: end of list
