diff --git a/src/ptr.rs b/src/ptr.rs
index 8525da0298fde69e5027c02757d24a8a48489178..57aeeb0284411d1178a9410c91832884ff7eb818 100644
--- a/src/ptr.rs
+++ b/src/ptr.rs
@@ -2,6 +2,7 @@ use parser::{parse, ParseError};
 use serde_json::Value;
 use std::fmt::{Display, Formatter};
 use std::fmt::Result as FmtResult;
+use std::fmt::Write;
 use std::marker::PhantomData;
 use std::ops::{Index, IndexMut};
 use std::str::FromStr;
@@ -94,6 +95,30 @@ impl<S: AsRef<str>, C: AsRef<[S]>> JsonPointer<S, C> {
             }
         }))
     }
+
+    /// Converts a JSON pointer to a string in URI Fragment Identifier
+    /// Representation, including the leading `#`.
+    pub fn uri_fragment(&self) -> String {
+        fn legal_fragment_byte(b: u8) -> bool {
+            match b {
+                0x21 | 0x24 | 0x26...0x3b | 0x3d | 0x3f...0x5a | 0x5f | 0x61...0x7a => true,
+                _ => false,
+            }
+        }
+
+        let mut s = "#".to_string();
+        for part in self.ref_toks.as_ref().iter() {
+            s += "/";
+            for b in part.as_ref().bytes() {
+                if legal_fragment_byte(b) {
+                    s.push(b as char)
+                } else {
+                    write!(s, "%{:02x}", b).unwrap()
+                }
+            }
+        }
+        s
+    }
 }
 
 impl<S: AsRef<str>> JsonPointer<S, Vec<S>> {
diff --git a/tests/parsing.rs b/tests/parsing.rs
index 4a16154ada6e234c5d05b5065062844adb1089ec..48b45398847fb45b210c412362a501233e458342 100644
--- a/tests/parsing.rs
+++ b/tests/parsing.rs
@@ -11,20 +11,20 @@ use regex::Regex;
 
 quickcheck! {
 
-/// Essentially, `unparse(parse("..."))` should be a no-op when not in URI
-/// Fragment Identifier Representation.
+/// Essentially, `unparse(parse("..."))` should be a no-op.
 fn faithful_parse(s: String) -> TestResult {
-    if s.chars().next() == Some('#') {
-        TestResult::discard()
+    let ok = match s.parse::<JsonPointer<_, _>>() {
+        Ok(ptr) => if s.chars().next() == Some('#') {
+            (s == ptr.uri_fragment())
+        } else {
+            (s == ptr.to_string())
+        },
+        Err(_) => return TestResult::discard(),
+    };
+    if ok {
+        TestResult::passed()
     } else {
-        match s.parse::<JsonPointer<_, _>>() {
-            Ok(ptr) => if s == ptr.to_string() {
-            TestResult::passed()
-            } else {
-                TestResult::failed()
-            },
-            Err(_) => TestResult::discard(),
-        }
+        TestResult::failed()
     }
 }
 
@@ -33,7 +33,7 @@ fn faithful_parse(s: String) -> TestResult {
 fn parses_all_valid(s: String) -> bool {
     lazy_static! {
         static ref JSON_POINTER_REGEX: Regex = Regex::new("^(/([^/~]|~[01])*)*$").unwrap();
-        static ref URI_FRAGMENT_REGEX: Regex = Regex::new("^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$").unwrap();
+        static ref URI_FRAGMENT_REGEX: Regex = Regex::new("^#(/([^A-Za-z0-9._!$&'()*+,;=@/?-]|~[01]|%[0-9a-fA-F]{2})*)*$").unwrap();
     }
 
     let matches_regex = JSON_POINTER_REGEX.is_match(&s) || URI_FRAGMENT_REGEX.is_match(&s);
diff --git a/tests/uri_fragment.rs b/tests/uri_fragment.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1ec8bf7f48afcd5b38dffae099b65f1569c61832
--- /dev/null
+++ b/tests/uri_fragment.rs
@@ -0,0 +1,16 @@
+extern crate json_pointer;
+
+use json_pointer::JsonPointer;
+
+macro_rules! assert_unparse {
+    ($expr:expr) => {
+        let ptr = $expr.parse::<JsonPointer<_, _>>().unwrap();
+        assert_eq!(ptr.uri_fragment(), $expr);
+    };
+}
+
+#[test]
+fn uri_fragment_unparses() {
+    assert_unparse!("#/");
+    assert_unparse!("#/per%25/%25cent");
+}