{"id":3126,"date":"2016-03-19T13:00:44","date_gmt":"2016-03-19T13:00:44","guid":{"rendered":"http:\/\/www.ericwhite.com\/home2\/bm8qcmjy\/public_html\/blog\/?page_id=3126"},"modified":"2016-03-19T13:00:44","modified_gmt":"2016-03-19T13:00:44","slug":"workaround-for-bad-link-issue-in-openxml-sdk","status":"publish","type":"page","link":"https:\/\/www.ericwhite.com\/blog\/workaround-for-bad-link-issue-in-openxml-sdk\/","title":{"rendered":"Workaround for Bad Link Issue in OpenXML SDK"},"content":{"rendered":"<div>\n<p>A number of people have reported a problem with the OpenXML SDK throwing an exception, &#8220;Invalid URI:The hostname could not be parsed.&#8221; This exception is actually coming from the Uri class. Since the OpenXML SDK is using System.IO.Packaging to get relationships for the document and System.IO.Packaging is throwing the exception when it tries to create the Uri object, it is impossible to open or modify the document using either of those. I have created a workaround in C# that allows you to modify or remove the bad links so that the document can then be opened successfully with the OpenXML SDK. If you just want the workaround without all the explanation, scroll down to the end of this post and you will see the code there. You can also see a video that I have created that quickly explains the situation and workaround.<\/p>\n<p><iframe loading=\"lazy\" title=\"Workaround for Bad Link Issue in OpenXML SDK\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/fZWyWWZs8JU?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>First, if you type the following into a Word document and press Enter, it will automatically make it a link:<\/p>\n<p>http:\/\/(<a href=\"http:\/\/www.bing.com\">www.bing.com<\/a>)<\/p>\n<p>As explained above, the Uri class will throw an exception when System.IO.Packaging processes the relationship for that link. This is seen when using the following code to open the document using the OpenXML SDK:<\/p>\n<p><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; document = WordprocessingDocument.Open(fileName, true);<\/span><\/p>\n<p>If you were to try using System.IO.Packaging directly, the exception wouldn&#8217;t occur immediately. For example:<\/p>\n<p><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; Package pkg = Package.Open(fileName);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; PackagePart docPart = pkg.GetPart(PackUriHelper.ResolvePartUri(new Uri(&#8220;\/&#8221;, UriKind.Relative), new Uri(&#8220;word\/document.xml&#8221;, UriKind.Relative)));<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; foreach (PackageRelationship item in docPart.GetRelationships())<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Uri test = item.TargetUri;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p>This code won&#8217;t throw the exception until the method &#8220;GetRelationships&#8221; is called. Unfortunately, that means that System.IO.Packaging cannot be used to correct the problem.<\/p>\n<p>In order to fix the file so that it can be opened, I needed to access the relationship file in the docx package directly. I used the DotNetZip library(<a href=\"http:\/\/dotnetzip.codeplex.com\/\">http:\/\/dotnetzip.codeplex.com\/<\/a>) to extract that file, modify it, and save it so that the document could be opened normally using the OpenXML SDK. Here is the C# code that does that:<\/p>\n<p><span style=\"font-family:courier new,courier;\">using System;<\/span><br \/><span style=\"font-family:courier new,courier;\">using System.Collections.Generic;<\/span><br \/><span style=\"font-family:courier new,courier;\">using System.Linq;<\/span><br \/><span style=\"font-family:courier new,courier;\">using System.IO;<\/span><br \/><span style=\"font-family:courier new,courier;\">using DocumentFormat.OpenXml.Packaging;<\/span><br \/><span style=\"font-family:courier new,courier;\">using Ionic.Zip;<\/span><br \/><span style=\"font-family:courier new,courier;\">using System.Xml.Linq;<\/span><\/p>\n<p><span style=\"font-family:courier new,courier;\">namespace BadLinkTest<\/span><br \/><span style=\"font-family:courier new,courier;\">{<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; class Program<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string fileName = &#8220;..\/..\/Link.docx&#8221;;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WordprocessingDocument document;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document = WordprocessingDocument.Open(fileName, true);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (UriFormatException)<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Clean(fileName, FixIt);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document = WordprocessingDocument.Open(fileName, true);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Process document here<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document.Close();<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static string FixIt(string old)<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return old.Replace(&#8220;(&#8220;, &#8220;&#8221;).Replace(&#8220;)&#8221;, &#8220;&#8221;);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Clean(string fileName, Func&lt;string, string&gt; fixUri)<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (ZipFile zip = ZipFile.Read(fileName))<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ZipEntry item = zip[&#8220;word\/_rels\/document.xml.rels&#8221;];<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryStream stream = new MemoryStream();<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; item.Extract(stream);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stream.Position = 0;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XElement doc = XElement.Load(new StreamReader(stream));<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool changed = false;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (XElement el in doc.Descendants()<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .Where(n =&gt; n.Attribute(&#8220;TargetMode&#8221;) != null &amp;&amp; n.Attribute(&#8220;TargetMode&#8221;).Value == &#8220;External&#8221;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; !Uri.IsWellFormedUriString(n.Attribute(&#8220;Target&#8221;).Value, UriKind.Absolute)))<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; el.Attribute(&#8220;Target&#8221;).Value = fixUri(el.Attribute(&#8220;Target&#8221;).Value);<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; changed = true;<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (changed)<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zip.UpdateEntry(item.FileName, doc.ToString());<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zip.Save();<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">&nbsp;&nbsp;&nbsp; }<\/span><br \/><span style=\"font-family:courier new,courier;\">}<\/span><\/p>\n<p>Notice that I am using Ionic.Zip.dll from DotNetZip. There are three methods here.<\/p>\n<p>The Main method is an example of how the code would look in your own program. It catches the UriFormatException and calls the Clean method to fix it. The Clean method requires a delegate to the function that will actually change the link. My version of the delegate, FixIt, removes any parenthesis from the link, since that is a common cause of the exception. Another option would be to return a dummy link, like &#8220;http:\/\/dummy.com&#8221;, no matter what the original link is.<\/p>\n<p>The Clean method opens the document as a Zip file and extracts the document relationships into a MemoryStream. That can then be loaded into an XElement object for processing. The foreach loop finds all external relationships that are not well formed Uri&#8217;s. Those are modified by calling the delegate method, then the modified file is updated in the document and saved. As long as FixIt returns a modified link that is valid, the document can then be opened using the OpenXML SDK, as shown in the Main method.<\/p>\n<p>It is possible to have invalid links in other relationship files (e.g. headers and footers). I expect that you would be able to extend this code to include other relationship files as needed, but I could extend this example to handle other relationship files in a general way, if there was interest. That code would be a lot more involved than this simple example.<\/p>\n<p>I hope this helps out everyone who has been struggling with this issue. Please let me know if you still have problems with this issue.<\/p>\n<div style=\"clear:both;\"><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>A number of people have reported a problem with the OpenXML SDK throwing an exception, &#8220;Invalid URI:The hostname could not be parsed.&#8221; This exception is actually coming from the Uri class. Since the OpenXML SDK is using System.IO.Packaging to get relationships for the document and System.IO.Packaging is throwing the exception when it tries to create [&hellip;]<\/p>\n","protected":false},"author":10567,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"_s2mail":"","footnotes":""},"class_list":["post-3126","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages\/3126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/users\/10567"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/comments?post=3126"}],"version-history":[{"count":1,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages\/3126\/revisions"}],"predecessor-version":[{"id":3127,"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/pages\/3126\/revisions\/3127"}],"wp:attachment":[{"href":"https:\/\/www.ericwhite.com\/blog\/wp-json\/wp\/v2\/media?parent=3126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}