質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

2回答

1385閲覧

JSでJSONをHTMLに変換したい

ikeyu.tp

総合スコア6

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2021/11/22 01:50

こんにちは。いつもお世話になっております。

状況

  • contenteditableでブロックエディタを作成しています。
  • 昨日に質問でHTMLをJSONに変換する方法について質問させていただきました。

解決したい内容

  • HTMLからJSONに変換する事は出来たのですが,JSONからHTMLに変換できません。
  • 下記のようなJSONからHTMLに変換する関数を作成したいです。

json

1{ 2 "type": "DIV", 3 "content": [ 4 { 5 "type": "SPAN", 6 "content": [ 7 "0", 8 { 9 "type": "SPAN", 10 "content": [ 11 "12345" 12 ], 13 "attributes": [ 14 { 15 "key": "data-color", 16 "value": "#f00" 17 }, 18 { 19 "key": "style", 20 "value": "color: rgb(255, 0, 0);" 21 } 22 ] 23 }, 24 "" 25 ], 26 "attributes": [] 27 }, 28 { 29 "type": "SPAN", 30 "content": [ 31 "6789" 32 ] 33 } 34 ], 35 "attributes": [ 36 { 37 "key": "data-key", 38 "value": "0" 39 }, 40 { 41 "key": "data-number", 42 "value": "0" 43 }, 44 { 45 "key": "data-type", 46 "value": "p" 47 }, 48 { 49 "key": "data-level", 50 "value": "0" 51 }, 52 { 53 "key": "data-align", 54 "value": "left" 55 } 56 ] 57}

html

1<div data-key="0" data-number="0" data-type="p" data-level="0"data-align="left"> 2  <span> 3    0 4    <span data-color="#f00" style="color: rgb(255, 0, 0);">12345</span> 5  </span> 6  <span>6789</span> 7</div>

検討した解決方法

  • 下記の関数でHTMLへの変換を試みましたが,上記のHTMLのような構造になりませんでした。
    • 一つ目のspan内に二つ目のspan(6789)も含まれてしまいます。

JavaScript

1/** 2* 3* @param parent appendする親要素 4* @param object 上記のJSON 5*/ 6function ToHTML(parent,object){ 7 if(object['type']){ 8 child=document.createElement(object['type']); 9 parent.appendChild(child); 10 for(let i=0;i<object['attributes'].length;++i){ 11 child.setAttribute(object['attributes'][i]['key'],object['attributes'][i]['value']); 12 } 13 if(object['content']){ 14 for(let i=0;i<object['content'].length;++i){ 15 if(object['content'][i]['type']){ 16 ToHTML(child,object['content'][i]); 17 }else{ 18 text=document.createTextNode(object['content'][i]); 19 child.appendChild(text); 20 } 21 } 22 } 23 } 24}

自力での解決や検索の仕方が悪いのかネットでも解決策を見つけられなかったのでご指南の程よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2021/11/22 03:16

JSONはそれがマストですか?attributesの持ち方が非効率的だと思いますが・・・
guest

回答2

0

以下の3点

  1. 最低限の修正版
  2. リファクタ案
  3. Reactコンポーネント案

の順に回答します。

1. 最低限の修正版

はじめに、質問の「検討した解決方法」にあるコードのfunction ToHTML(parent,object)に最小限の修正して、とりあえずサンプルのJSONに対しては意図通り動くようにします。

修正点は3つあり、それぞれ、コメントに修正内容を書いています。

diff

1 function ToHTML(parent,object){ 2 if(object['type']){ 3- child=document.createElement(object['type']); 4+ const child=document.createElement(object['type']); // (1) const を追加 5 parent.appendChild(child); 6- for(let i=0;i<object['attributes'].length;++i){ 7- child.setAttribute(object['attributes'][i]['key'],object['attributes'][i]['value']); 8+ if (object['attributes']) { // (2) attributes が無い場合もあるので、if が必要 9+ for(let i=0;i<object['attributes'].length;++i){ 10+ child.setAttribute(object['attributes'][i]['key'],object['attributes'][i]['value']); 11+ } 12 } 13 if(object['content']){ 14 for(let i=0;i<object['content'].length;++i){ 15 if(object['content'][i]['type']){ 16 ToHTML(child,object['content'][i]); 17 }else{ 18- text=document.createTextNode(object['content'][i]); 19+ const text=document.createTextNode(object['content'][i]); // (3) const を追加 20 child.appendChild(text); 21 } 22 } 23 } 24 } 25 }

2. リファクタ案

上記の 1. 最低限の修正版 をリファクタしたものが以下です。

javascript

1function ToHTML(parent, object) { 2 const { type, attributes, content } = object; 3 const node = type ? document.createElement(type) : document.createTextNode(object); 4 5 if (type) { 6 if (attributes) { 7 attributes.forEach(({ key, value }) => { 8 node.setAttribute(key, value); 9 }); 10 } 11 if (content) { 12 content.forEach(child => ToHTML(node, child)); 13 } 14 } 15 16 parent.appendChild(node); 17}

これは、1. 最低限の修正版を、以下の諸点でリファクタしたものです。

  • objectを分割代入して、type, attributes, content という変数を作ることによって、object['type'] といったブラケット[・・・]によるプロパティ取得を書かなくて済むようにした。

  • typeによって作られるHTML要素の変数名を child から node に変更して、child という変数名は、content の各要素をforEachで取り出したときの変数名とした。

  • 変数名nodeには、typeの有無によって、HTML要素のノードかテキストノードのいずれか(に対する参照)が入るようにした。

  • for ループを forEach に置き換えた。

3. Reactコンポーネント案

質問をさかのぼってみると、contenteditableでのインライン要素指定について というご質問があり、この質問にはタグReact が付いていますね。もしReactで作ることも検討しているなら、DOMを直接操作するのではなく、与えられたJSONからReactコンポーネントを作ってこれをrenderするほうが望ましいです。

その場合、ざっくりですが、こんな感じのものを作ればよいかと思います。

jsx

1const DecoratedText = ({ data }) => { 2 if (data === null || typeof data === 'string') { 3 return data; 4 } 5 6 const { type, attributes, content } = data; 7 8 if (!type) { 9 return null; 10 } 11 12 const Outer = type.toLowerCase(); 13 14 const props = Object.fromEntries((attributes || []).map(({key, value}) => 15 [key, key === 'style' ? styleProps(value) : value] 16 )); 17 18 return ( 19 <Outer {...props}> 20 {content && content.map(data => <DecoratedText key={randomStr()} data={data}/>)} 21 </Outer> 22 ); 23}

補助的に使っている関数は以下です。

javascript

1// ランダム文字列生成関数 2const randomStr = () => (Math.random() + 1).toString(36).substring(2); 3 4// style 属性の文字列から、styleオブジェクトを作って返す関数 5const styleProps = (styleStr) => 6 styleStr.split(';').filter(s => s).reduce((o, s) => { 7 const [k, v] = s.split(':'); 8 o[k] = v.trim(); 9 return o; 10 }, {});

投稿2021/11/22 03:45

編集2021/11/22 22:14
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

HTMLからJSONに変換する事は出来た

中途半端で、元に戻せそうもないように見えます
せめてこんな感じで

javascript

1<script> 2const html=`<div data-key="0" data-number="0" data-type="p" data-level="0"data-align="left"> 3  <span data-test="xxx"> 4    0 5    <span data-color="#f00" style="color: rgb(255, 0, 0);">12345</span> 6  </span> 7  <span>6789</span> 8</div>`; 9const template = document.createElement("template"); 10template.innerHTML = html; 11const nodes=[...template.content.children]; 12const getElements=(n)=>[...n].map(x=>(x.nodeType==1 13 ?{ 14 type:x.nodeName, 15 content:getElements(x.childNodes), 16 attributes:x.attributes?Object.fromEntries([...x.attributes].map(x=>[x.name,x.value])):{}, 17 } 18 :{ 19 type:x.nodeName, 20 content:x.textContent, 21 } 22 )); 23// 一旦JSONに保持 24const json=JSON.stringify(getElements(nodes)); 25console.log(json); 26/* 27[{"type":"DIV","content":[{"type":"#text","content":"\n  "},{"type":"SPAN","content":[{"type":"#text","content":"\n    0\n    "},{"type":"SPAN","content":[{"type":"#text","content":"12345"}],"attributes":{"data-color":"#f00","style":"color: rgb(255, 0, 0);"}},{"type":"#text","content":"\n  "}],"attributes":{"data-test":"xxx"}},{"type":"#text","content":"\n  "},{"type":"SPAN","content":[{"type":"#text","content":"6789"}],"attributes":{}},{"type":"#text","content":"\n"}],"attributes":{"data-key":"0","data-number":"0","data-type":"p","data-level":"0","data-align":"left"}},{"type":"DIV","content":[],"attributes":{}}] 28*/ 29 30const appendContent=(node,content,attr)=>{ 31 if(content){ 32 content.map(x=> 33 x.type=="#text"? 34 document.createTextNode(x.content): 35 appendContent(document.createElement(x.type),x.content,x.attributes)) 36 .forEach(x=>node.appendChild(x)); 37 } 38 if(attr){ 39 Object.entries(attr).forEach(x=>{ 40 node.setAttribute(x[0],x[1]); 41 }); 42 } 43 return node; 44}; 45 46const result=JSON.parse(json).reduce((x,y)=>{ 47 const node=document.createElement(y.type); 48 const content=y.content; 49 const attr=y.attributes; 50 x.appendChild(appendContent(node,content,attr)); 51 return x; 52},document.createDocumentFragment()); 53 54 55window.addEventListener('DOMContentLoaded', ()=>{ 56 [...result.children].forEach(x=>document.querySelector('#view').appendChild(x)); 57 console.log(document.querySelector('#view').innerHTML); 58/* 59<div data-key="0" data-number="0" data-type="p" data-level="0" data-align="left"> 60  <span data-test="xxx"> 61    0 62    <span data-color="#f00" style="color: rgb(255, 0, 0);">12345</span> 63  </span> 64  <span>6789</span> 65</div> 66*/ 67}); 68</script> 69 70<div id="view"></div>

投稿2021/11/22 03:25

編集2021/11/22 05:00
yambejp

総合スコア114784

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問