views:

421

answers:

2

Okay, Now I have an unordered list here:

<ul id="mycustomid">
    <li><a href="url of Item A" title="sometitle">Item A</a>
        <ul class="children">
           <li><a href="url of Child1 of A" title="sometitle">Child1 of A</a>
               <ul class="children">
                 <li><a href="url of Grandchild of A" title="sometitle">Grandchild of A</a>
                    <ul class="children">
                       <li><a href="url of Grand Grand child of A" title="sometitle">Grand Grand child of A</a></li>
                    </ul>
                </li>
               </ul>
           </li>
        </ul>
    </li>
    <li><a href="url of Item B" title="sometitle">Item B</a></li>
    <li><a href="url of Item C" title="sometitle">Item C</a></li>
</ul>

Basically, I want to just convert this data into a JSON entity. I want to get this done in Jquery and I think I'm having a really tough time doing it. The above list is just an example and in reality, my list would ideally have more number of children and probably be 'n' levels deep (Meaning, it will have grandchildren of grandchildren of grandchildren...or more) I've lost countless hours of sleep on this and I don't think I'm going anywhere :(

I want to extract these things: The text inside the anchor, the url of the anchor and the title of the anchor and put them onto a JSON entity

The JSON format for my list above is something like this:

{
        name: "Item A",
        url: "url of Item A",
        title: "sometitle",
        children: [{
                   name: "Child1 of A",
                   url: "url of Child1 of A",
                   title: "sometitle",
                   children: [{
                                name: "Grandchild of A",
                                url: "url of Grandchild of A",
                                title: "sometitle",
                                children: [{
                                           name: "Grand Grand child of A",
                                           url: "url of Grand Grand child of A",
                                           title: "sometitle",
                                           children: []
                                           }]
                              }]
                   }]
},
           {
            name: "Item B",
            url: "url of Item B",
            title: "sometitle",
            children: []
           },
           {
            name: "Item C",
            url: "url of Item C",
            title: "sometitle",
            children: []
           }

Some useful references:

Javascript solution: http://stackoverflow.com/questions/3158265/traversing-unordered-lists-using-javascript-jquery

^ This one probably works, but the format of the JSON output I need is as shown above and not what this script outputs :(

Other references:

http://stackoverflow.com/questions/2793688/how-do-i-put-unordered-list-items-into-an-array

http://jsfiddle.net/yS6ZJ/1/

http://jsfiddle.net/CLLts/

http://jsfiddle.net/cWnwt/

Someone please help :(
Been breaking my head for many many sleepless nights..(P.s - It took me about 40+ mins to write this entire page along with the code)

Thank you.

A: 

Ah, a fun little recursive exercise. I had a moment for this and here’s how I would do it. This works many levels deep recursively, but assumes that your data is not deep enough to explode the memory (recursion breaks in browsers if it is too deep). Should be fine for at least 10 levels or so.

I tested this out, seems it works, just save this in a HTML file and you should be fine.

Sorry there are not too many comments (well, technically speaking, none at all :) , this assumes you read jQuery and JS code fine. If you have questions, just ask in a comment and I’d be happy to explain.

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>Recursive list processor example</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"&gt;&lt;/script&gt;
    <script type="text/javascript">

$(document).ready(function() {

    var out = [];

    function processOneLi(node) {       

        var aNode = node.children("a:first");
        var retVal = {
            "title": aNode.attr("title"),
            "url": aNode.attr("href"),
            "name": aNode.text()
        };

        node.find("> .children > li").each(function() {
            if (!retVal.hasOwnProperty("children")) {
                retVal.children = [];
            }
            retVal.children.push(processOneLi($(this)));
        });

        return retVal;
    }

    $("#mycustomid").children("li").each(function() {
        out.push(processOneLi($(this)));
    });


    console.log("got the following JSON from your HTML:", JSON.stringify(out));

});

    </script>
</head>
<body>
<ul id="mycustomid">
    <li><a href="http://example.com/urlOfItemA" title="sometitle">Item A</a>
        <ul class="children">
           <li><a href="http://example.com/urlOfItemAChild1" title="sometitle">Child1 of A</a>
               <ul class="children">
                 <li><a href="http://example.com/urlOfItemAGrandchild" title="sometitle">Grandchild of A</a>
                    <ul class="children">
                       <li><a href="http://example.com/urlOfItemAGrandGrandChild" title="sometitle">Grand Grand child of A</a></li>
                    </ul>
                </li>
               </ul>
           </li>
        </ul>
    </li>
    <li><a href="http://example.com/urlOfItemB" title="sometitle2">Item '"" B</a></li>
    <li><a href="http://example.com/urlOfItemC" title="sometitle3">Item C</a></li>
</ul>
</body>
</html>
Jaanus
Sorry Jaanus, I wasn't at my desk. Thank you very much for your effort, the code you gave works simply perfect and you're my blessing in disguise and you saved me a lot of headache!! Thank you very much!!! :)
imaginonic
You should also mark the accepted answer, whichever you accept, to be a good Stackoverflow citizen :)
Jaanus
However, it doesn't output the JSON in my format? for example I have quotes on the attributes like for example:"name":"something""title":"something"Instead I need it to be like name:"something"title:"something"Thank you
imaginonic
JSON standard says that the object keys must be in double quotes. I am using a standard JSON outputter by Douglas Crockford, which is the industry standard from transforming Javascript objects to JSON strings. So, what you are asking for is non-standard invalid JSON. Why would you need it?
Jaanus
1) I am new here, I'm learning everything from experts like you, so sorry for that answer accepting issue :) Thank you for pointing it out :)2) The sample input I have for my application is as I've described above..I din't know it was invalid JSON, but it uses that format only :(
imaginonic
1) no worries, we were all new once :) 2) I see what you mean. The point of standards like JSON is that apps input and output it in a standard way. The right thing to do would be to fix your app so that it accepts standard valid JSON. If that is not possible, you may be able to get tvanfonsson's code to work. You can see how it outputs custom JSON as string, and you may hack it to produce what you need. But really, you should fix this other app. There are other ways but they are really backwards bad practice.
Jaanus
Thank you, thank you and thank you Jaanus, you did a really wonderful job and yes I WILL correct this issue regarding the standards. Let me *bow* to you, great angel :) Thank you for everything - your time, Ideas and energy. Won't forget your timely help. :)
imaginonic
+1  A: 

Seems like a recursive solution using one function for collections of list elements (children) and one for a list element would work. Note I'm assuming that you want it formatted as a string and that it's really represented as an array.

$(function() {
    var json = formatListElements( $('#mycustomid > li') );
});

function formatListElements( elems )
{
    var contents = [] 
    $.each( elems, function( index, elem ) {
        contents[index] = formatListElement( elem );
    }
    return '[' + contents.join(', ') + ']';
}

function formatListElement( elem )
{
   var anchor = $(elem).children('a:first');
   return '{ "name": "' + quote( anchor.text() )
                + '", "url": "' + quote( anchor.attr('href') )
                + '", "title": "' + quote( anchor.attr('title') )
                + '", "children": ' + formatListElements( $(elem).find('> ul > li')
        + '}';
}

function quote( str )
{
    return str.replace( /"/g, '\\\"' );
}
tvanfosson
This outputs invalid JSON because: 1) you are not quoting attribute values 2) you are not escaping the ' or " characters in strings, that you would quote the attribute values with. Otherwise good.
Jaanus
@Jaanus -- yeah, just a quick answer and completely untested. I've added the quotes around the attributes and quoting of quotes in strings.
tvanfosson
The way you did it now, you might get too much quoting.. not sure if ' has to be quoted inside a JSON "blah ' blah" string or not? :)
Jaanus
@Jaanus -- probably right and only need to quote the double quotes. I don't think it will hurt though.
tvanfosson
Thank you tvanfosson, I am still testing your code and I think it doesn't work (yet). Thank you very much for your input :)
imaginonic
@tv - I checked JSON standard and Doug Crockford's JSON stringifier. JSON standard talks explicitly about double quotes, and so double quotes are the only ones you need to escape in a string, not single quotes. It WILL hurt, as you will have extra backslash characters in the output.
Jaanus
@Jaanus - as it's not necessary, I'll remove it, but having a quoted quote won't hurt. The backslash, since there's only one of them, will still be interpreted as a quote character and the result of quoting a single quote, is a single quote.
tvanfosson
@tv - thanks for the clarification. I didn't realize that a backslash still works as a quote character in this context also for single quotes, but tested it out, you're right and I was a bit overzealous. So yeah, whether or not you have the quoting code for single 's there, doesn't really matter.
Jaanus