Js 从 json 中提取信息

Js extract information from a json

提问人:Paul 提问时间:9/22/2023 最后编辑:Paul 更新时间:9/27/2023 访问量:89

问:

我有以下非常复杂和详细的json。

只有当 包含以下文本并且包含以下 props () 时,我才需要从 json 推断信息(文本)。resource-id"com.shazam.android:id/"["title", "subtitle", "datetime"]

如果最后一个元素不包含 则不插入 THEN。datetimeresorceIdFinal

下面是我想得到的 json 示例。

在这里你可以找到我使用的json,由于字符空间,我无法在这里插入它。

友情链接: https://pastebin.com/raw/hFLmP2T8

const check = (obj, keys) => !keys.some((el) => !obj.hasOwnProperty(el));

const isObjectEmpty = (objectName) => {
  return (
    objectName &&
    Object.keys(objectName).length === 0 &&
    objectName.constructor === Object
  );
};

const getChildren = (el, replaceText, props, resorceIdFinal) => {
  var arr = [];

  el.forEach((el) => {
    let resorceId = getResorceId(el.attributes, replaceText, props, resorceIdFinal);
    if(!isObjectEmpty(resorceId)) arr.push(resorceId);   
    //console.log(arr);
    getChildren(el.children, replaceText, props, resorceIdFinal);
  });
  
  return arr;
};


const getResorceId = (el, replaceText, props, resorceIdFinal) => {
  var o = {};
  if (el["resource-id"] !== undefined) {
    var resorceId = el["resource-id"].replace(replaceText, "");
    if (props.includes(resorceId)) {
      o = { [resorceId]: el.text };
    }
  }
  return o;
};

function readPro(json, replaceText, props, filterA, resorceIdFinal) {
  var arr = [];

  json.forEach((el) => {
    arr.push(getChildren(el.children, replaceText, props, resorceIdFinal));
  });
  
  console.log(arr)

  filtered = arr.filter(
    ((s) => (o) =>
      ((k) => !s.has(k) && s.add(k))(filterA.map((k) => o[k]).join("|")))(
        new Set()
      )
  );

  return filtered;
}


var res = readPro(
  a.children,
  "com.shazam.android:id/",
  ["title", "subtitle", "datetime"],
  ["title", "subtitle"],
  "datetime"
);

console.log(res);

我想得到的 Json 结果:

[
   {
        "title": "Believe",
        "subtitle": "Chuther",
        "datetime": "12 giu, 16:42"
   },
   {
        "title": "시작 (Inst.)",
        "subtitle": "Gaho",
        "datetime": "12 giu, 16:42"
   },
   {
        "title": "Give It Up",
        "subtitle": "KC and the Sunshine Band",
        "datetime": "12 giu, 16:41"
   },
   {
        "title": "GRAVITY",
        "subtitle": "Jong Ho",
        "datetime": "12 giu, 16:41"
    }
]

你能帮我一把吗?

编辑:

const isObjectEmpty = (objectName) =>
(
  objectName &&
  Object.keys(objectName).length === 0 &&
  objectName.constructor === Object
);


const getResorceId = (el, replaceText, props, value) => {
  if (el["resource-id"] !== undefined) {
    var resorceId = el["resource-id"].replace(replaceText, "");
    if (props.includes(resorceId)) {
      return { [resorceId]: el[value] };
    }
  }
  return {};
};

const getChildren = (el, replaceText, props, value) => {
  var arr = [];
  el.forEach((el) => {
    let resorceId = getResorceId(el.attributes, replaceText, props, value);
    var o = {}
    if (!isObjectEmpty(resorceId)) {
      arr.push(resorceId);
      for (const [_, value] of Object.entries(arr)) {
        for (const [key, value2] of Object.entries(value))
          o[key] = value2
      }
      arr = [o]
    }

    arr.push(...getChildren(el.children, replaceText, props, value));
  });

  //console.log(arr.length, arr)
  //if(arr.length === 1 && !Object.hasOwn(...arr, resorceIdFinal)) arr = []

  return arr
};

function readPro(json, replaceText, props, filterA, value) {
  var arr = [];
  json.forEach((el) => {
    arr.push(...getChildren(el.children, replaceText, props, value));
  });

  filtered = arr.filter(
    ((s) => (o) =>
      ((k) => !s.has(k) && s.add(k))(filterA.map((k) => o[k]).join("|")))(
        new Set()
      )
  );

  return filtered;
}

var rA = readPro(
  a.children,
  "com.shazam.android:id/",
  ["title", "subtitle", "datetime"],
  ["title", "subtitle"],
  "text"
);
JavaScript 数组 JSON 对象 筛选器

评论

1赞 ControlAltDel 9/22/2023
你不想使用 JSON.parse?
0赞 Paul 9/22/2023
@ControlAltDel:Json解析是为了什么?问题是结构太嵌套了,以至于我做错了或忘记了什么。
1赞 David 9/22/2023
调试时,哪个特定操作未达到预期效果?该操作中使用的观察到的运行时值是什么?结果是什么?预期的结果是什么?为什么?
0赞 Paul 9/22/2023
@David:我得到一个空数组。最终结果可以在最后的帖子中找到。我认为问题是递归调用,它应该返回一些东西,但我在我编写的代码中做错了什么。
0赞 GetSet 9/23/2023
在检查了您的pastebin文件后,我确实看到了它的结构。显然,您想从该 json 中嵌入的数据构建某种播放列表,这可能是 self 中的播放列表。我认为解决这个问题的最好方法是线性扫描。有一个明确的模式使你能够这样做。

答:

1赞 David 9/23/2023 #1

此函数以递归方式调用自身,但忽略该调用的结果:

const getChildren = (el, replaceText, props, resorceIdFinal) => {
  var arr = [];

  el.forEach((el) => {
    let resorceId = getResorceId(el.attributes, replaceText, props, resorceIdFinal);
    if(!isObjectEmpty(resorceId)) arr.push(resorceId);   
    //console.log(arr);
    getChildren(el.children, replaceText, props, resorceIdFinal);
  });
  
  return arr;
};

返回值 不会执行任何操作。也许目的是将该结果添加到数组中?getChildren(el.children, replaceText, props, resorceIdFinal)arr

const getChildren = (el, replaceText, props, resorceIdFinal) => {
  var arr = [];

  el.forEach((el) => {
    let resorceId = getResorceId(el.attributes, replaceText, props, resorceIdFinal);
    if(!isObjectEmpty(resorceId)) arr.push(resorceId);   
    //console.log(arr);
    arr.push(...getChildren(el.children, replaceText, props, resorceIdFinal));
  });
  
  return arr;
};

请注意,这里有很多代码,这个答案不能保证这是所示代码中的唯一问题。但它至少看起来与所描述的问题最相关。建议使用调试器来缩小任何其他问题的范围,如果发现任何问题,请单独询问。

评论

0赞 Paul 9/23/2023
感谢您的回复,我设法让它工作,我想让它更简单、更干净。如果有机会,可以看一看。以下是测试它的 json 文件: pastebin.com/raw/jmHMnfvg
1赞 GetSet 9/23/2023 #2

在注释的延续中,如果您的要求没有发生重大变化,则可以通过“逐行”处理 json 字符串来提取要提取的数据。

通过查看所需的输出和源 json,json 结构中的属性将成为 title、subtitle 和 datetime。因此,当任何命名的属性出现(线性)时,算法会将其保存为最后遇到的属性。接下来,当遇到所需的哨兵时,以下每个哨兵:texttext

com.shazam.android:id/title
com.shazam.android:id/subtitle
com.shazam.android:id/datetime

...将以前(或上次)保存的属性指定为该属性。
记录日期时间时,假定这是记录的末尾,并将其推送到数组上,以便可以以相同的方式逐步构建下一条记录(如果有)。
text

其余的则通过代码本身和代码注释来解释。

这是一个工作的例子。将 json 复制并粘贴到 textarea 中,然后单击按钮。json 必须是完整的,因为它首先尝试解析它,然后字符串化,以便将其正确分隔成行。当您已经知道输入已分隔成行时,可以注释掉这些步骤。

function extract( input_json ) {    
        
    // In the event that the source json is not already broken into lines:
    try {
        let _parsed = JSON.parse( input_json );
        input_json = JSON.stringify(_parsed , null, 1);
    }
    catch (e) {
        console.log("Error parsing json");
        return false;
    }
    
    
    // Turn into an array, one line per index
    
    let json_lines = input_json.split("\n");
    
    
    // Now onto a linear scan/search to gather the pertinent data
    
    let output = []; // this will hold the end result
    
    let last_passed_text = ''; // this will be the last "text" attribute's value encountered
    
    
    let current_record = {}; // this will hold the current record being built progressively
    
    
    for (let i = 0; i < json_lines.length; i++) {
        
        let textPos = json_lines[i].indexOf('"text"');
        
        if ( textPos > -1) {
        
            let _firstColon = json_lines[i].indexOf(":");
            
            if (_firstColon > textPos) {
        
            // split by colon since `"text" :`  indicates definition desired
            
                let relevantPart = json_lines[i].substring( _firstColon + 1).trim(); // characters after colon to end of string, trimmed
            
            try {
                relevantPart = decodeURIComponent( relevantPart ); // just in case
            }
            catch {}
            
                // assume that characters after colon behave as `"abcdef",` and if not it doesn't matter, e.g. boolean values
                
                let token = relevantPart.substring(1,relevantPart.length-2); // get value between the assumed double quotes, -2 because of trailing comma, if any
                
                last_passed_text = token.toString(); // de-ref
                
                // console.log("last_passed_text",last_passed_text);
                
            }
        }
        
        if ( json_lines[i].indexOf("resource-id") > -1) {
            
            
            
            if ( json_lines[i].indexOf("com.shazam.android:id/title") > -1) {
                
                // presence of title always indicates where to start collecting
                
                current_record = {};
                current_record.title = last_passed_text.toString(); // de-ref
                
                //console.log("title", current_record.title);
                
                // console.log( json_lines[i], last_passed_text );
                
                
            }
            else if ( json_lines[i].indexOf("com.shazam.android:id/subtitle") > -1) {
                current_record.subtitle = last_passed_text.toString(); // de-ref
                
                // console.log( json_lines[i], last_passed_text );
            }
            else if ( json_lines[i].indexOf("com.shazam.android:id/datetime") > -1) {
                current_record.datetime = last_passed_text.toString(); // de-ref
                
                // presence of datetime always indicates where to end collecting
                
                output.push( JSON.parse(JSON.stringify( current_record )) );
                
                // console.log( json_lines[i], last_passed_text );
                
                
            }
        }
    
    }
    
    
    // console.log(output);
    
    return output;
}
Paste json here: (input)<br>
<textarea id="input-json" rows=15 cols=50></textarea>
<hr>
<button type="button" onclick="
    console.log( 
        extract( 
            document.getElementById('input-json').value
        ) 
    );">Extract To Console</button>

评论

0赞 Paul 9/23/2023
感谢您的回复,我尝试了您的代码,以相同的方式生成了另一个json,我收到以下错误: 这是测试它的json文件: pastebin.com/raw/jmHMnfvg{ "message": "Uncaught URIError: URI malformed", "filename": "https://stacksnippets.net/js", "lineno": 62, "colno": 32 }
0赞 GetSet 9/23/2023
@Paul我现在就试试。好的,我明白了。与 decodeURIComponent 失败有关。解决方法是将其包装在尝试捕获中。我会编辑答案。
0赞 Paul 9/23/2023
好的,我想我已经找到了解决方案,尽管仍有一些问题需要解决。如果你想看一下,你可以看看编辑。
0赞 GetSet 9/23/2023
可能还有很多问题需要修复,或者至少是作为预防措施:(1)代码假设标题、副标题、日期时间将按该顺序出现。如果他们总是这样做,那么就没有问题。(2) 如果代码没有遇到日期时间,则不会推送记录。...但是,如果源 json 遵循该模式,则这些都不是问题。
0赞 Paul 9/23/2023
模式是一样的,它们的生成方式相同。我不明白为什么你的代码不能与其他json一起使用。