{"id":9446,"date":"2014-05-22T13:02:24","date_gmt":"2014-05-22T18:02:24","guid":{"rendered":"http:\/\/jianmingli.com\/wp\/?p=9446"},"modified":"2014-05-22T13:02:24","modified_gmt":"2014-05-22T18:02:24","slug":"jquery-file-upload","status":"publish","type":"post","link":"https:\/\/jianmingli.com\/wp\/?p=9446","title":{"rendered":"JQuery File Upload"},"content":{"rendered":"<div class='toc wptoc'>\n<h2>Contents<\/h2>\n<ol class='toc-odd level-1'>\n\t<li>\n\t\t<a href=\"#Database_Table\">Database Table<\/a>\n\t<\/li>\n\t<li>\n\t\t<a href=\"#RESTful_Service\">RESTful Service<\/a>\n\t\t<ol class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Directory_Structure\">Directory Structure<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#JDeveloper_Libraries_and_Classpath\">JDeveloper Libraries and Classpath<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#web.xml\">web.xml<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Jersey_RESTful_Service_Java_File\">Jersey RESTful Service Java File<\/a>\n\t\t\t<\/li>\n\t\t<\/ol>\n\t<li>\n\t\t<a href=\"#jQuery_Script\">jQuery Script<\/a>\n\t<\/li>\n\t<li>\n\t\t<a href=\"#Test_HTML_Page\">Test HTML Page<\/a>\n\t\t<ol class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Upload_Page\">Upload Page<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Download_Page\">Download Page<\/a>\n\t\t\t<\/li>\n\t\t<\/ol>\n\t<li>\n\t\t<a href=\"#References\">References<\/a>\n\t<\/li>\n<\/ol>\n<\/ol>\n<\/div>\n<div class='wptoc-end'>&nbsp;<\/div>\n<span id=\"Database_Table\"><h2>Database Table<\/h2><\/span>\n<pre lang=\"sql\">\r\nCREATE TABLE \"TEST_ATTACHMENT\" \r\n(\t\r\n\t\"ID\" VARCHAR2(20 BYTE) NOT NULL ENABLE, \r\n\t\"ATTACHMENT\" BLOB, \r\n\tCONSTRAINT \"TEST_ATTACHMENT_PK\" PRIMARY KEY (\"ID\")\r\n);\r\n<\/pre>\n<span id=\"RESTful_Service\"><h2>RESTful Service<\/h2><\/span>\n<p>* See <a href=\"?p=9342\">this post<\/a> on how to setup RESTful service using JDeveloper<\/p>\n<span id=\"Directory_Structure\"><h3>Directory Structure<\/h3><\/span>\n<span id=\"\"><h6><a href=\"https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_dirStructure_1.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_dirStructure_1-209x300.jpg\" alt=\"\" title=\"jersey_restful_dirStructure_1\" width=\"209\" height=\"300\" class=\"aligncenter size-medium wp-image-9463\" srcset=\"https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_dirStructure_1-209x300.jpg 209w, https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_dirStructure_1.jpg 228w\" sizes=\"auto, (max-width: 209px) 100vw, 209px\" \/><\/a><\/h6><\/span>\n<span id=\"JDeveloper_Libraries_and_Classpath\"><h3>JDeveloper Libraries and Classpath<\/h3><\/span>\n<p>* Make sure to include <strong>matching<\/strong> versions of <strong>jersey-multipart.jar<\/strong> and <strong>mimepull.jar<\/strong><\/p>\n<span id=\"_1\"><h6><a href=\"https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_classpath_1.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_classpath_1-300x204.jpg\" alt=\"\" title=\"jersey_restful_classpath_1\" width=\"300\" height=\"204\" class=\"aligncenter size-medium wp-image-9462\" srcset=\"https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_classpath_1-300x204.jpg 300w, https:\/\/jianmingli.com\/wp\/wp-content\/uploads\/2013\/12\/jersey_restful_classpath_1.jpg 600w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/h6><\/span>\n<span id=\"web.xml\"><h3>web.xml<\/h3><\/span>\n<pre lang=\"xml\">\r\n<?xml version = '1.0' encoding = 'windows-1252'?>\r\n<web-app xmlns=\"http:\/\/java.sun.com\/xml\/ns\/javaee\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\r\n         xsi:schemaLocation=\"http:\/\/java.sun.com\/xml\/ns\/javaee http:\/\/java.sun.com\/xml\/ns\/javaee\/web-app_2_5.xsd\"\r\n         version=\"2.5\">\r\n  <servlet>\r\n    <servlet-name>jersey<\/servlet-name>\r\n    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer<\/servlet-class>\r\n    <load-on-startup>1<\/load-on-startup>\r\n  <\/servlet>\r\n  <servlet-mapping>\r\n    <servlet-name>jersey<\/servlet-name>\r\n    <url-pattern>\/jersey\/*<\/url-pattern>\r\n  <\/servlet-mapping>\r\n<\/web-app>\r\n<\/pre>\n<span id=\"Jersey_RESTful_Service_Java_File\"><h3>Jersey RESTful Service Java File<\/h3><\/span>\n<p>* This is the POJO Java file with Jersey annotation:<\/p>\n<pre lang=\"java\">\r\npackage test.att.rs;\r\n\r\n\r\nimport com.sun.jersey.core.header.FormDataContentDisposition;\r\nimport com.sun.jersey.multipart.FormDataParam;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\n\r\nimport java.util.Collections;\r\nimport java.util.List;\r\n\r\nimport javax.naming.Context;\r\nimport javax.naming.InitialContext;\r\nimport javax.naming.NamingException;\r\n\r\nimport javax.ws.rs.Consumes;\r\nimport javax.ws.rs.GET;\r\nimport javax.ws.rs.POST;\r\nimport javax.ws.rs.Path;\r\nimport javax.ws.rs.PathParam;\r\nimport javax.ws.rs.Produces;\r\nimport javax.ws.rs.QueryParam;\r\nimport javax.ws.rs.core.MediaType;\r\nimport javax.ws.rs.core.Response;\r\n\r\nimport org.apache.commons.io.IOUtils;\r\n\r\nimport test.att.ejb.AttSessionEJB;\r\nimport test.att.ejb.TestAttachment;\r\n\r\n\r\n@Path(\"\/attRS\")\r\npublic class AttRS {\r\n    private static String[] whiteList = {\".pdf\", \".doc\"};\r\n    private AttSessionEJB attBean;\r\n    private String mappedName = \r\n        \"FLASApp-TestAttachment-AttSessionEJB#test.att.ejb.AttSessionEJB\";\r\n\r\n    private boolean inWhiteList(String fileName){\r\n        for (String str : whiteList){\r\n            if (fileName.endsWith(str)){\r\n                return true;\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n    \r\n    public AttRS() {\r\n        super();\r\n        Context ctx = null;\r\n        try {\r\n            ctx = getIniCtx();\r\n            attBean = (AttSessionEJB)ctx.lookup(mappedName);\r\n        } catch (NamingException e) {\r\n            throw new RuntimeException(e);\r\n        }\r\n\r\n    }\r\n\r\n    @GET\r\n    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })\r\n    public List<TestAttachment> listAttachments() {\r\n        System.out.println(\"listAttachments...\");\r\n        List<TestAttachment> all = attBean.getTestAttachmentFindAll();\r\n        \/\/TODO sort list\r\n        Collections.sort(all);\r\n        return all;\r\n    }\r\n    \r\n    @GET\r\n    @Path(\"{attId}\")\r\n    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })\r\n    public TestAttachment findById(@PathParam(\"attId\") String id){\r\n        System.out.println(\"findById: \" + id);\r\n        TestAttachment att = attBean.findByPrimaryKey(id);\r\n        att.setAttachment(null);\r\n        return att;\r\n    }\r\n\r\n    @POST\r\n    @Consumes(MediaType.MULTIPART_FORM_DATA)\r\n    public Response create(\r\n        @FormDataParam(\"attId\") String id, \r\n        @FormDataParam(\"file\") InputStream is, \r\n        @FormDataParam(\"file\") FormDataContentDisposition fileDetails) {\r\n        System.out.println(\"Adding attachment with id \" + id \r\n                           + \" and file name: \" + fileDetails.getFileName());\r\n        if (!inWhiteList(fileDetails.getFileName())){\r\n            return Response.status(400).entity(\"File type not supported.\").build();\r\n        }\r\n        TestAttachment att = new TestAttachment();\r\n        att.setId(id);\r\n        try {\r\n            byte[] ba = IOUtils.toByteArray(is);\r\n            System.out.println(\"InputStream size: \" + ba.length);\r\n            att.setAttachment(ba);\r\n            att.setFileName(fileDetails.getFileName());\r\n        } catch (IOException e) {\r\n            System.err.println(e.getMessage());\r\n            return Response.status(400).entity(\"Error saving file\").build();\r\n        }\r\n        attBean.persistTestAttachment(att);\r\n        return Response.status(200).entity(\"OK\").build();\r\n    }\r\n    \r\n    @GET\r\n    @Path(\"\/downloadFile\")\r\n    @Produces(MediaType.APPLICATION_OCTET_STREAM)\r\n    public Response downloadFile(@QueryParam(value = \"attId\") String id){\r\n        TestAttachment att = attBean.findByPrimaryKey(id);\r\n        System.out.println(\"Attachment size: \" + att.getAttachment().length);\r\n        String fn = att.getId();\r\n        if (att != null && att.getFileName() != null){\r\n            fn = att.getFileName();\r\n        }\r\n        return Response\r\n            .ok(att.getAttachment(), MediaType.APPLICATION_OCTET_STREAM)\r\n            .header(\"content-disposition\", \"attachment; filename = \" + att.getFileName()).build();\r\n    }\r\n\r\n    private static Context getIniCtx() throws NamingException {\r\n        return new InitialContext();\r\n    }\r\n\r\n}\r\n<\/pre>\n<span id=\"jQuery_Script\"><h2>jQuery Script<\/h2><\/span>\n<p>* Download into <em>js<\/em> folder (see previous directory structure)<br \/>\n&#8211; <em>jquery-1.8.3.js<\/em><br \/>\n&#8211; <em>jquery.form.js<\/em><br \/>\n&#8211; <em>purl.js<\/em><br \/>\n* jQuery script:<\/p>\n<pre lang=\"bash\">\r\n\r\nvar rootURL = \"http:\/\/127.0.0.1:7101\/FLASApp-TestAttachment-context-root\/jersey\/attRS\";\r\nvar currentAtt;\r\n\r\n\/**\r\n * Find all attachments\r\n *\/\r\nfunction findAll() {\r\n    console.log(\"findAll...\");\r\n    $.ajax( {\r\n        type : 'GET', \r\n        url : rootURL, \r\n        dataType : \"json\",\r\n        success : renderList\r\n    });\r\n}\r\n\r\n\/**\r\n * Find attachment by Id\r\n *\/\r\nfunction findById(){\r\n    var id = $.url().param('attId');\r\n    console.log(\"findById: \" + id);\r\n    $.ajax( {\r\n        type : 'GET', \r\n        url : rootURL + '\/' + id, \r\n        dataType : \"json\",\r\n        success : function(data){\r\n            console.log(\"findById success\");\r\n            currentAtt = data;\r\n            renderDetails(currentAtt);\r\n        },\r\n        error : function (jqXHR, textStatus, errorThrown) {\r\n            console.log('findById error: ' + textStatus + errorThrown);\r\n            alert('findById error: ' + textStatus + errorThrown);\r\n        }\r\n    });\r\n}\r\n\r\n\/**\r\n * Render attachment details\r\n * @param att \r\n *\/\r\nfunction renderDetails(att){\r\n    console.log(\"renderDetails...\");\r\n    $('#attId').empty();\r\n    $('#attId').append(att.id);\r\n    $('#fileName').empty();\r\n    $('#fileName').append(downloadUrl(att));\r\n}\r\n\r\n\/**\r\n * Render a list of all attachments\r\n * @param data \r\n *\/\r\nfunction renderList(data) {\r\n    console.log(\"renderList...\");\r\n    \/\/ JAX-RS serializes an empty list as null, and a 'collection of one' as an object (not an 'array of one')\r\n    var list = data == null ? [] : (data instanceof Array ? data : [data]);\r\n\r\n    $('#attList li').remove();\r\n    $.each(list, function (index, att) {\r\n        var attUrl = rootURL + '\/downloadFile?attId=' + att.id;\r\n        var item = '<li><a href=\"p2.html?attId=' + att.id + '\">' + att.id + '<\/a>';\r\n        if (att.fileName){\r\n            item += '    ' + downloadUrl(att);\r\n        }\r\n        item += '<\/li>';\r\n        $('#attList').append(item);\r\n    });\r\n}\r\n\r\n\/**\r\n * Build a file download URL\r\n *\/\r\nfunction downloadUrl(att) {\r\n    var attUrl = rootURL + '\/downloadFile?attId=' + att.id;\r\n    var dlurl;\r\n    if (att.fileName){\r\n        dlurl = '<a href=\"' + attUrl + '\">' + att.fileName + '<\/a>';\r\n    }\r\n    return dlurl;\r\n}\r\n\r\n\/**\r\n * Upload an attachment\r\n * Requires IE 10 and above since IE 9 and below do not support\r\n * file API and FormData.\r\n *\/\r\nfunction uploadAttachment() {\r\n    console.log('uploadAttachment...');\r\n    var attId = $('#attId').val();\r\n    var file = $('#file').get(0).files[0];\r\n    var formData = new FormData();\r\n    formData.append('attId', attId);\r\n    formData.append('file', file);\r\n    console.log('file size: ' + file.size);\r\n    $.ajax( {\r\n        type : 'POST', \r\n        url : rootURL, \r\n        data : formData, \r\n        processData : false, \r\n        contentType : false, \r\n        success : function (data, textStatus, jqXHR) {\r\n            console.log(\"File uploaded successfully.\");\r\n            findAll();\r\n        },\r\n        error : function (jqXHR, textStatus, errorThrown) {\r\n            console.log('uploadAttachment error: ' + textStatus + errorThrown);\r\n            alert('uploadAttachment error: ' + textStatus + errorThrown);\r\n        }\r\n    });\r\n}\r\n\r\n\/**\r\n * Upload an attachment using jQuery Form Plugin\r\n * which supports ajax file upload for IE9 and below\r\n *\/\r\nfunction uploadAttachmentWithJQueryForm() {\r\n    console.log('uploadAttachmentWithJQueryForm...');\r\n    var options = {\r\n        type : 'POST', \r\n        url : rootURL, \r\n        success : function (data, textStatus, jqXHR) {\r\n            console.log(\"File uploaded successfully.\");\r\n            findAll();\r\n        },\r\n        error : function (jqXHR, textStatus, errorThrown) {\r\n            console.log('uploadAttachment error: ' + textStatus + errorThrown);\r\n            alert('uploadAttachment error: ' + textStatus + errorThrown);\r\n        }\r\n    };\r\n\r\n    $('#attForm').ajaxSubmit(options);\r\n}\r\n<\/pre>\n<span id=\"Test_HTML_Page\"><h2>Test HTML Page<\/h2><\/span>\n<span id=\"Upload_Page\"><h3>Upload Page<\/h3><\/span>\n<p>* This is the test html page (<em>p1.html<\/em>) used to upload file:<\/p>\n<pre lang=\"xml\">\r\n<!DOCTYPE HTML>\r\n<html>\r\n    <head>\r\n        <meta http-equiv=\"Content-Type\" content=\"text\/html; charset=windows-1252\"><\/meta>\r\n    <\/head>\r\n    <body>\r\n    \r\n        <ul id=\"attList\"><\/ul>\r\n\r\n        <form id=\"attForm\" name=\"attForm\" enctype=\"multipart\/form-data\" method=\"POST\">\r\n            ID: <input type=\"text\" name=\"attId\" id=\"attId\"\/>\r\n            <br\/>\r\n            Attachment: <input type=\"file\" id=\"file\" name=\"file\" size=\"30\"\/>\r\n            <br\/>\r\n            <input type=\"submit\" id=\"btnSave\" name=\"btnSave\" value=\"Submit\"\/>\r\n        <\/form>\r\n\r\n        <script src=\"js\/jquery-1.8.3.js\" type=\"text\/javascript\"><\/script>\r\n        <script src=\"js\/jquery.form.js\" type=\"text\/javascript\"><\/script>\r\n        <script src=\"js\/att.js\" type=\"text\/javascript\"><\/script>\r\n        <script type=\"text\/javascript\">\r\n            $(document).ready(function (){\r\n                $('#attForm').submit(function(){\r\n                    \/\/uploadAttachment(); \/\/ Upload attachment with FileAPI and FormData. Need IE10.\r\n                    uploadAttachmentWithJQueryForm(); \/\/ Upload attachment with jQuery Form plugin.\r\n                    return false;\r\n                });\r\n                findAll();\r\n            });\r\n        <\/script>\r\n    <\/body>\r\n<\/html>\r\n<\/pre>\n<span id=\"Download_Page\"><h3>Download Page<\/h3><\/span>\n<p>* This is the download test page:<\/p>\n<pre lang=\"xml\">\r\n<!DOCTYPE HTML>\r\n<html>\r\n    <head>\r\n        <title>Attachment Details<\/title>\r\n    <\/head>\r\n    <body>\r\n    \r\n        ID: <label id=\"id\">ID<\/label><br\/>\r\n        File Name: <label id=\"fileName\">fileName<\/label><br\/>\r\n        \r\n        <script src=\"js\/jquery-1.8.3.js\" type=\"text\/javascript\"><\/script>\r\n        <script src=\"js\/purl.js\" type=\"text\/javascript\"><\/script>\r\n        <script src=\"js\/att.js\" type=\"text\/javascript\"><\/script>\r\n        <script type=\"text\/javascript\">findById();<\/script>\r\n    <\/body>\r\n<\/html>\r\n<\/pre>\n<p>* Test download URL: <a href=\"http:\/\/127.0.0.1:7101\/FLASApp-TestAttachment-context-root\/p2.html?attId=15\">http:\/\/127.0.0.1:7101\/FLASApp-TestAttachment-context-root\/p2.html?attId=15<\/a><\/p>\n<span id=\"References\"><h2>References<\/h2><\/span>\n<p>* <a href=\"http:\/\/coenraets.org\/blog\/2011\/12\/restful-services-with-jquery-and-java-using-jax-rs-and-jersey\/\">RESTful services with jQuery and Java using JAX-RS and Jersey<\/a><br \/>\n* <a href=\"http:\/\/www.jquery4u.com\/plugins\/jquery-file-upload-plugins\/\">6 Nice jQuery File Upload Plugins<\/a><br \/>\n* <a href=\"http:\/\/stackoverflow.com\/questions\/19148065\/how-to-post-binary-file-from-jquery-client-to-java-server-using-rest\">post<\/a><br \/>\n* <a href=\"http:\/\/stackoverflow.com\/questions\/166221\/how-can-i-upload-files-asynchronously-with-jquery\">post<\/a><br \/>\n* <a href=\"http:\/\/examples.javacodegeeks.com\/enterprise-java\/rest\/jersey\/jersey-file-upload-example\/\">Jersey File Upload Example<\/a><br \/>\n* <a href=\"http:\/\/malsup.com\/jquery\/form\/\">jQuery Form Plugin<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Database Table CREATE TABLE &#8220;TEST_ATTACHMENT&#8221; ( &#8220;ID&#8221; VARCHAR2(20 BYTE) NOT NULL ENABLE, &#8220;ATTACHMENT&#8221; BLOB, CONSTRAINT &#8220;TEST_ATTACHMENT_PK&#8221; PRIMARY KEY (&#8220;ID&#8221;) ); RESTful Service * See this post on how to setup RESTful service using JDeveloper Directory Structure JDeveloper Libraries and Classpath &hellip; <a href=\"https:\/\/jianmingli.com\/wp\/?p=9446\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[458],"tags":[640,205,460,459],"class_list":["post-9446","post","type-post","status-publish","format-standard","hentry","category-jquery","tag-jquery","tag-restful","tag-up","tag-upload"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8cRUO-2sm","_links":{"self":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/9446","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9446"}],"version-history":[{"count":17,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/9446\/revisions"}],"predecessor-version":[{"id":10025,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/9446\/revisions\/10025"}],"wp:attachment":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9446"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9446"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9446"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}