使用 WKWebView 显示本地 XML 文档

Show local XML document with WKWebView

我正在尝试使用 WKWebView 显示 XML 文件。有没有人设法显示它?我已经设置了一个完整的示例应用程序(见下文)。在iOS9.3iPhone4S模拟器上测试得到结果

LoadFileUrl()

string targetFilepath = Init("simplexsl.xml");
Init("simple.xsl");

NSUrl url = new NSUrl("file:///" + targetFilepath, false);
NSUrl readAccessUrl = new NSUrl("file:///" + Path.GetDirectoryName(targetFilepath), true);
webview.LoadFileUrl(url, readAccessUrl);

结果:白页

LoadHtmlString()

string targetFilepath = Init("simplexsl.xml");
Init("simple.xsl");

NSData documentData = NSData.FromFile(targetFilepath);

if(documentData != null && documentData.Length > 0)
{
    NSString htmlString = NSString.FromData(documentData, NSStringEncoding.UTF8);
    webview.LoadHtmlString(htmlString, readAccessUrl);
}

结果:显示 xml 的内容(没有来自 xml 的标签),但未应用样式。

NSUrl readAccessUrl = new NSUrl("file:///" + Path.GetDirectoryName(targetFilepath), true);

NSString htmlString = new NSString(File.ReadAllText(targetFilepath));
webview.LoadHtmlString(htmlString, readAccessUrl);

结果:显示 xml 的内容(没有来自 xml 的标签),但未应用样式。

LoadData()

string targetFilepath = Init("simplexsl.xml");
Init("simple.xsl");

NSUrl readAccessUrl = new NSUrl("file:///" + Path.GetDirectoryName(targetFilepath), true);

NSData documentData = NSData.FromFile(targetFilepath);

if(documentData != null && documentData.Length > 0)
{
    webview.LoadData(documentData, "text/xml", "utf-8", readAccessUrl);
}

结果:白页

LoadRequest()

string targetFilepath = Init("simplexsl.xml");
Init("simple.xsl");

NSUrl url = new NSUrl("file:///" + targetFilepath, false);
webview.LoadRequest(new NSUrlRequest(url));

结果:白页

路径不同

var documents = NSFileManager.DefaultManager.GetUrls(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomain.User) [0].Path;
var caches = Path.Combine (documents, "Caches");

结果:白页

var caches = Path.Combine(System.IO.Path.GetTempPath(), "www");
Directory.CreateDirectory(caches);

结果:白页

资源包

string folder = NSBundle.MainBundle.BundlePath;
string source = Path.Combine(folder, "simplexsl.xml");
webview.LoadFileUrl(new NSUrl(source,false), new NSUrl(folder, true))

结果:白页

Link

现在我加载了一个 html,里面有一个 link

<a href="simplexsl.xml">XML Link</a>

结果:html页面加载完成,点击link进入白页,如果xsl在xml中被注释掉则xml不显示造型

在线

webview.LoadRequest(new NSUrlRequest(new NSUrl("http://www.w3schools.com/xml/simplexsl.xml")));

结果:WKWebView 可以显示 XML file 如果允许它加载非 https 资源。

没有 XSL

simplexsl.xml

中注释掉下面一行
<?xml-stylesheet type="text/xsl" href="simple.xsl" ?>

并使用

webview.LoadFileUrl(url, readAccessUrl);

结果:显示 xml 的内容(没有 xml 的标签),但未应用样式。所以似乎存在 basepath 问题,但仅限于 XSL 文件?

当我使用 LoadFileUrl() 时,显示相对引用图片的 html 文件确实有效。但是对于 xml 文件似乎有问题。对于一个简短的测试,UIWebView 似乎处理得更好。

那么它是如何正确完成的呢?


基本设置

public override void ViewDidLoad()
{
    base.ViewDidLoad();

    var webview = new WKWebView(View.Bounds, new WKWebViewConfiguration());
    View.AddSubview(webview);

    // code here
}

private string Init(string filename)
{
    string sourceFilepath = Path.Combine(NSBundle.MainBundle.BundlePath, filename);
    var library = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments).Replace("Documents", "Library");
    var caches = Path.Combine(library, "Caches");
    var targetFilepath = Path.Combine(caches, filename);

    if (CopyFile(sourceFilepath, targetFilepath))
        return targetFilepath;

    return string.Empty;
}

private bool CopyFile(string sourceFilepath, string targetFilepath)
{
    if (File.Exists(sourceFilepath))
    {
        File.Copy(sourceFilepath, targetFilepath, true);
        return true;
    }
    return false;
}

targetFilepath 类似于 /Users/some-user/Library/Developer/CoreSimulator/Devices/8808B80D-D232-4599-B776-139409C9DDB8/data/Containers/Data/Application/BC84CFD0-A805-417C-912F-C2B07834C822/Library/Caches/simplexsl.xml

simplexsl.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="simple.xsl" ?>
<breakfast_menu>
  <food>
    <name>Belgian Waffles</name>
    <price>.95</price>
    <description>two of our famous Belgian Waffles with plenty of real maple syrup</description>
    <calories>650</calories>
  </food>
  <food>
    <name>Strawberry Belgian Waffles</name>
    <price>.95</price>
    <description>light Belgian waffles covered with strawberries and whipped cream</description>
    <calories>900</calories>
  </food>
  <food>
    <name>Berry-Berry Belgian Waffles</name>
    <price>.95</price>
    <description>light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>
    <calories>900</calories>
  </food>
  <food>
    <name>French Toast</name>
    <price>.50</price>
    <description>thick slices made from our homemade sourdough bread</description>
    <calories>600</calories>
  </food>
  <food>
    <name>Homestyle Breakfast</name>
    <price>.95</price>
    <description>two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
    <calories>950</calories>
  </food>
</breakfast_menu>

simple.xsl

<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
    <xsl:for-each select="breakfast_menu/food">
      <div style="background-color:teal;color:white;padding:4px">
        <span style="font-weight:bold">
          <xsl:value-of select="name"/> -
        </span>
        <xsl:value-of select="price"/>
      </div>
      <div style="margin-left:20px;margin-bottom:1em;font-size:10pt">
        <p>
          <xsl:value-of select="description"/>
          <span style="font-style:italic">
            (<xsl:value-of select="calories"/> calories per serving)
          </span>
        </p>
      </div>
    </xsl:for-each>
  </body>
</html>

如果你post你的解决方案在Objective-C或Swift.

中就没问题

我有种不好的预感,出于安全考虑,本地文件(尽管在同一目录中)不允许进行 XSL 转换。如果我在 Chrome 中打开文件,我会得到

Unsafe attempt to load URL file:///C:/some/path/Projects/TestWKWebView/TestWKWebView/Resources/simple.xsl from frame with URL file:///C:/some/path/Projects/TestWKWebView/TestWKWebView/Resources/simplexsl.xml. 'file:' URLs are treated as unique security origins.

Firefox 运行良好。在 Edge 中我得到

XSLT8690: The system cannot locate the object specified.

来自 here 的非常有趣的评论:

Interesting fact: Safari on Mac (8.0) works just fine with local XSLT, but Safari on iOS (8) simulator (and I suppose on iOS as well) I got the same error like in Chrome "Domains, protocols and ports must match.". But if I'm to implement UIWebView in the app and load XML with XSLT everything works OK.

经过一番研究,似乎有以下解决方案:

  • 使用UIWebView
  • 使用本地网络服务器(例如查看 here
  • 或许借助 libxslt 自己完成 XSLT(参见 here or here, also available as NuGet package