这应该是一个简单的问题,但是由于某种原因,我碰到了砖墙。
我有一个复杂程度未知的JSON对象,其中可能包含其他对象,数组或字符串属性。
作为一个例子,我可能有一个像这样的对象:
let json = {
id: "foo",
rows: [
{
class: "blah",
cols: [
{
class: "haha"
},{
class: "bar"
},{
class: "baz"
}
]
},
{
class: "yada"
}
]
}
*注(为清楚起见):对象的结构可能大不相同。它的行和列可能颠倒了,或者嵌套得更深了,或者根本没有。在任何对象上可能有或没有ID或类*
我需要遍历该对象并将其“转换”为html结构,如下所示:
<div id = "foo" data-type = "rows">
<div class = "blah" data-type = "cols">
<div class = "haha"></div>
<div class = "bar"></div>
<div class = "baz"></div>
</div>
<div class = "yada"></div>
</div>
每个对象都是一个(可能是嵌套的)div,字符串属性是该div上的属性,而数组键则表示类型。
这是我目前所拥有的:
function iterateJsonToHTML(frame) {
let content = document.createElement("div");
let iterator = function(frame, content) {
for (let [key, value] of Object.entries(frame)) {
if (Array.isArray(value) || (typeof value === "object" && value !== null) ) {
if (Array.isArray(value)) {
iterator(value, content);
} else if (typeof value === "object") {
let element = document.createElement("div");
content = content.appendChild(element);
iterator(value, content);
}
} else {
// dealing with attributes here. Not sure how to parse multiple of these.
}
}
};
iterator(frame, content);
return content;
}
这将创建一个HTML结构,而不是我期望的结构:
<div>
<div>
<div>
<div>
<div></div>
</div>
</div>
<div></div>
</div>
</div>
div
的数量正确,它们只是以某种方式旋转了。
所以我的问题很简单:
我的递归哪里出问题了?
答案 0 :(得分:2)
您可以使用此非常简单的单行功能render
注意:如果要在同一行中使用div属性(id,class,数据类型),只需将所有第一个验证都放在同一行
const obj = {
id: "foo",
rows: [
{
class: "blah",
cols: [
{
class: "haha"
},{
class: "bar"
},{
class: "baz"
}
]
},
{
class: "yada"
}
]
}
const render = (obj) =>
`
<div
${ obj.class ? `class="${obj.class}"` : '' }
${ obj.id ? `id="${obj.id}"` : '' }
${ obj.rows
? 'data-type="rows"'
: obj.cols
? 'data-type="cols"'
: '' }
>
${
(obj.cols || obj.rows || [])
.map(childObj =>
render(childObj)
).join('')
}
</div>
`
console.log(render(obj))
答案 1 :(得分:1)
对不起,但是我发现您的方法有点难以理解。我认为您应该尽可能使用DOM API,并且考虑到输入对象的潜在复杂性,请保持代码尽可能的清晰明了。
我建议使用递归方法,其中一次处理一个元素的所有详细信息(id,class等),然后处理其子元素,并在返回新元素之前附加这些子元素。这样,您还可以确保覆盖任何深度。
对我来说似乎很优雅:)
希望有帮助。
let json = {
id: "foo",
rows: [
{
class: "blah",
cols: [
{ class: "haha"},
{ class: "bar" },
{ class: "baz" }
]
}, {
class: "yada"
}
]
}
function transform(element) {
const { id, rows, cols, class: className } = element
const $element = document.createElement('div')
if ( id ) $element.setAttribute('id', id)
if ( className ) $element.classList.add(className)
let children = []
if ( rows ) {
$element.dataset.type = 'rows'
children = rows.map(transform)
}
if ( cols ) {
$element.dataset.type = 'cols'
children = cols.map(transform)
}
children.forEach(function append(child) {
$element.appendChild(child)
})
return $element
}
const $json = transform(json)
console.log($json.outerHTML)
答案 2 :(得分:1)
您的原始代码段很容易使用-输入格式中的子元素存储为数组,因此您只需要在创建子div之前对其进行迭代:
function iterateJsonToHTML(frame) {
let root = document.createElement("div");
let iterator = function(frame, content) {
for (let [key, value] of Object.entries(frame)) {
if (Array.isArray(value)) {
content.setAttribute("data-type", key);
for (let item of value) {
const element = document.createElement("div");
content.appendChild(element);
iterator(item, element);
}
} else {
content.setAttribute(key, value);
}
}
};
iterator(frame, root);
return root;
}
答案 3 :(得分:1)
此代码假定字符串将成为div的属性。数组将变成几个div。
function appendDOM( json, element ) {
let child = document.createElement('div');
for (const property in json) {
if( typeof json[property] === 'string' ) {
child.setAttribute( property, json[property] );
} else if( Array.isArray( json[property] ) ) {
for( let i=0; i< json[property].length; i++ ) {
appendDOM( json[property][i], child );
}
}
}
element.appendChild( child );
}
let json = {id: "foo",rows: [{class: "blah",cols: [{class: "haha"},{class: "bar"},{class: "baz"}]},{class: "yada", style:"background: yellow"}]}
appendDOM( json, document.getElementById('section') );
div{
border: 1px solid red;
padding: 5px;
margin: 5px;
text-align : center;
}
#foo::before{
content: 'foo';
}
.blah::before{
content: 'blah';
}
.haha::before{
content: 'haha';
}
.bar::before{
content: 'bar';
}
.baz::before{
content: 'baz';
}
.yada::before{
content: 'yada';
}
<section id='section'></section>
答案 4 :(得分:0)
为简单起见,我使用HTML concat代替元素创建。
default_args = {
"owner": "airflow",
"depends_on_past": False,
'start_date': airflow.utils.dates.days_ago(n=0,minute=1),
'on_failure_callback': send_task_failed_msg_to_slack,
'sla': timedelta(minutes=1),
"retries": 0,
"pool": 'canary',
'priority_weight': 1
}
dag = airflow.DAG(
dag_id='sla_test',
default_args=default_args,
sla_miss_callback=send_sla_miss_message_to_slack,
schedule_interval='*/5 * * * *',
catchup=False,
max_active_runs=1,
dagrun_timeout=timedelta(minutes=5)
)
def sleep():
""" Sleep for 2 minutes """
time.sleep(90)
LOGGER.info("Slept for 2 minutes")
def simple_print(**context):
""" Prints a message """
print("Hello World!")
sleep = PythonOperator(
task_id="sleep",
python_callable=sleep,
dag=dag
)
simple_task = PythonOperator(
task_id="simple_task",
python_callable=simple_print,
provide_context=True,
dag=dag
)
sleep >> simple_task
let json = {
id: "foo",
rows: [{
class: "blah",
cols: [{
class: "haha"
}, {
class: "bar"
}, {
class: "baz"
}]
},
{
class: "yada"
}
]
}
function iterateJsonToHTML(frame) {
let html = "";
html += '<div id = "' + frame.id + '" data-type = "rows">';
for (let i = 0; i < frame.rows.length; i++) {
html += '<div class = "' + frame.rows[i].class + '" data-type = "cols">';
if (frame.rows[i].cols) {
for (let j = 0; j < frame.rows[i].cols.length; j++) {
html += '<div class = "' + frame.rows[i].cols[j].class + '"></div>';
}
}
html += '</div>';
}
html += '</div>';
document.getElementById('content').innerHTML = html;
}
iterateJsonToHTML(json);
console.log(document.getElementById('content').innerHTML)
答案 5 :(得分:0)
有点学术练习,但这里是使用 object-scan
的迭代解决方案// const objectScan = require('object-scan');
const json = {
id: 'foo',
rows: [
{
class: 'blah',
cols: [
{ class: 'haha' },
{ class: 'bar' },
{ class: 'baz' }
]
},
{ class: 'yada' }
]
};
const r = objectScan(['', '**'], {
reverse: false,
beforeFn: (state) => {
// eslint-disable-next-line no-param-reassign
state.context = [];
},
afterFn: (state) => {
state.result = state.context.join('\n');
},
breakFn: ({ depth, value, context }) => {
if (typeof value !== 'string' && !Array.isArray(value)) {
const properties = Object
.entries(value)
.map(([k, v]) => (typeof v === 'string' ? `${k} = "${v}"` : `data-type = "${k}"`));
context.push(`${' '.repeat(depth)}<div ${properties.join(' ')}>`);
}
},
filterFn: ({ depth, value, context }) => {
if (typeof value !== 'string' && !Array.isArray(value)) {
context.push(`${' '.repeat(depth)}</div>`);
}
}
})(json);
console.log(r);
/* =>
<div id = "foo" data-type = "rows">
<div class = "blah" data-type = "cols">
<div class = "haha">
</div>
<div class = "bar">
</div>
<div class = "baz">
</div>
</div>
<div class = "yada">
</div>
</div>
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@16.0.2"></script>
免责声明:我是object-scan
的作者