Your Digital Media Has Never Looked So Good

 
lbell
Topic Author
Posts: 28
Joined: Wed Sep 03, 2014 7:36 pm

Trouble with parsing

Tue Sep 09, 2014 3:52 pm

I had a working channel a couple of days ago but when i added an if statement to the InitCategoryFeedConnection() I got these errors.
Any ideas would be helpful.
I also added a searchscreen...I'm not sure if that would cause this though.

------ Running ------
created feed connection for http://www.map2life.com/roku/xml/categoriesfeed.xml
url: http://www.map2life.com/roku/xml/categoriesfeed.xml
Took: 372ms
Parse Took: 0ms
begin category node parsing
number of categories: 5
ParseCategoryNode: category
category: Video Sermons | Short description of video sermon category
categoryLeaf: Video sermon categoryleaf 1 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Video sermon categoryleaf 2 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Video sermon categoryleaf 3 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Video sermon categoryleaf 4 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
added new child node
ParseCategoryNode: category
category: Israeli Intelligence Reports | Israeli Intelligence reorts description
categoryLeaf: Israeli Intelligence Reports categoryleaf 1 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Israeli Intelligence Reports categoryleaf 2 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Israeli Intelligence Reports categoryleaf 3 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Israeli Intelligence Reports categoryleaf 4 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
added new child node
ParseCategoryNode: category
category: Audio Sermons | Short description of Audio sermon category
categoryLeaf: Audio sermon categoryleaf 1 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Audio sermon categoryleaf 2 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Audio sermon categoryleaf 3 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Audio sermon categoryleaf 4 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
added new child node
ParseCategoryNode: category
category: Music | Short description of Music category
categoryLeaf: Audio sermon categoryleaf 1 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Audio sermon categoryleaf 2 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Audio sermon categoryleaf 3 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
categoryLeaf: Audio sermon categoryleaf 4 []
ParseCategoryNode: categoryLeaf
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
added new child node
ParseCategoryNode: specialCategory
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid
added new child node
Traversing: 5ms





Here's the source code.
'******************************************************
' Set up the category feed connection object
' This feed provides details about top level categories
'******************************************************
Function InitCategoryFeedConnection() As Object

    conn = CreateObject("roAssociativeArray")
   
    conn.UrlPrefix   = "http://www.map2life.com/roku"
  if IsHD() = true then
    conn.UrlCategoryFeed = conn.UrlPrefix + "/xml/categoriesfeed.xml"
  else
    conn.UrlCategoryFeed = conn.UrlPrefix + "/xmlsd/categoriesfeedsd.xml"
  end if
    conn.Timer = CreateObject("roTimespan")

    conn.LoadCategoryFeed    = load_category_feed
    conn.GetCategoryNames    = get_category_names

    print "created feed connection for " + conn.UrlCategoryFeed
    return conn

End Function


'*********************************************************
'** Create an array of names representing the children
'** for the current list of categories. This is useful
'** for filling in the filter banner with the names of
'** all the categories at the next level in the hierarchy
'*********************************************************
Function get_category_names(categories As Object) As Dynamic

    categoryNames = CreateObject("roArray", 100, true)

    for each category in categories.kids
        'print category.Title
        categoryNames.Push(category.Title)
    next

    return categoryNames

End Function


'******************************************************************
'** Given a connection object for a category feed, fetch,
'** parse and build the tree for the feed.  the results are
'** stored hierarchically with parent/child relationships
'** with a single default node named Root at the root of the tree
'******************************************************************
Function load_category_feed(conn As Object) As Dynamic

    http = NewHttp(conn.UrlCategoryFeed)

    Dbg("url: ", http.Http.GetUrl())

    m.Timer.Mark()
    rsp = http.GetToStringWithRetry()
    Dbg("Took: ", m.Timer)

    m.Timer.Mark()
    xml=CreateObject("roXMLElement")
    if not xml.Parse(rsp) then
         print "Can't parse feed"
        return invalid
    endif
    Dbg("Parse Took: ", m.Timer)

    m.Timer.Mark()
    if xml.category = invalid then
        print "no categories tag"
        return invalid
    endif

    if islist(xml.category) = false then
        print "invalid feed body"
        return invalid
    endif

    if xml.category[0].GetName() <> "category" then
        print "no initial category tag"
        return invalid
    endif

    topNode = MakeEmptyCatNode()
    topNode.Title = "root"
    topNode.isapphome = true

    print "begin category node parsing"

    categories = xml.GetChildElements()
    print "number of categories: " + itostr(categories.Count())
    for each e in categories
        o = ParseCategoryNode(e)
        if o <> invalid then
            topNode.AddKid(o)
            print "added new child node"
        else
            print "parse returned no child node"
        endif
    next
    Dbg("Traversing: ", m.Timer)

    return topNode

End Function

'******************************************************
'MakeEmptyCatNode - use to create top node in the tree
'******************************************************
Function MakeEmptyCatNode() As Object
    return init_category_item()
End Function


'***********************************************************
'Given the xml element to an <Category> tag in the category
'feed, walk it and return the top level node to its tree
'***********************************************************
Function ParseCategoryNode(xml As Object) As dynamic
    o = init_category_item()

    print "ParseCategoryNode: " + xml.GetName()
    'PrintXML(xml, 5)

    'parse the curent node to determine the type. everything except
    'special categories are considered normal, others have unique types
    if xml.GetName() = "category" then
        print "category: " + xml@title + " | " + xml@description
        o.Type = "normal"
        o.Title = xml@title
        o.Description = xml@Description
        o.ShortDescriptionLine1 = xml@Title
        o.ShortDescriptionLine2 = xml@Description
        o.SDPosterURL = xml@sd_img
        o.HDPosterURL = xml@hd_img
    elseif xml.GetName() = "categoryLeaf" then
        o.Type = "normal"
    elseif xml.GetName() = "specialCategory" then
        if invalid <> xml.GetAttributes() then
            for each a in xml.GetAttributes()
                if a = "type" then
                    o.Type = xml.GetAttributes()[a]
                    print "specialCategory: " + xml@type + "|" + xml@title + " | " + xml@description
                    o.Title = xml@title
                    o.Description = xml@Description
                    o.ShortDescriptionLine1 = xml@Title
                    o.ShortDescriptionLine2 = xml@Description
                    o.SDPosterURL = xml@sd_img
                    o.HDPosterURL = xml@hd_img
                endif
            next
        endif
    else
        print "ParseCategoryNode skip: " + xml.GetName()
        return invalid
    endif

    'only continue processing if we are dealing with a known type
    'if new types are supported, make sure to add them to the list
    'and parse them correctly further downstream in the parser
    while true
        if o.Type = "normal" exit while
        if o.Type = "special_category" exit while
        print "ParseCategoryNode unrecognized feed type"
        return invalid
    end while

    'get the list of child nodes and recursed
    'through everything under the current node
    for each e in xml.GetBody()
        name = e.GetName()
        if name = "category" then
            print "category: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.ShortDescriptionLine1 = xml@Description
            kid.SDPosterURL = xml@sd_img
            kid.HDPosterURL = xml@hd_img
            o.AddKid(kid)
        elseif name = "categoryLeaf" then
            print "categoryLeaf: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.Feed = e@feed
            o.AddKid(kid)
        elseif name = "specialCategory" then
            print "specialCategory: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.sd_img = e@sd_img
            kid.hd_img = e@hd_img
            kid.Feed = e@feed
            o.AddKid(kid)
        endif
    next

    return o
End Function


'******************************************************
'Initialize a Category Item
'******************************************************
Function init_category_item() As Object
    o = CreateObject("roAssociativeArray")
    o.Title       = ""
    o.Type        = "normal"
    o.Description = ""
    o.Kids        = CreateObject("roArray", 100, true)
    o.Parent      = invalid
    o.Feed        = ""
    o.IsLeaf      = cn_is_leaf
    o.AddKid      = cn_add_kid
    return o
End Function


'********************************************************
'** Helper function for each node, returns true/false
'** indicating that this node is a leaf node in the tree
'********************************************************
Function cn_is_leaf() As Boolean
    if m.Kids.Count() > 0 return true
    if m.Feed <> "" return false
    return true
End Function


'*********************************************************
'** Helper function for each node in the tree to add a
'** new node as a child to this node.
'*********************************************************
Sub cn_add_kid(kid As Object)
    if kid = invalid then
        print "skipping: attempt to add invalid kid failed"
        return
     endif
   
    kid.Parent = m
    m.Kids.Push(kid)
End Sub

 
User avatar
RokuRobB
Posts: 85
Joined: Tue Jul 10, 2012 3:07 pm

Re: Trouble with parsing

Fri Sep 12, 2014 2:08 pm

My guess is that the XML structure of the SD vs. the HD feed is different, but the parsing code in your channel expects them to be the same. Then you get to a certain node level in the parsing, the elements in the enumeration you are looping on are invalid or otherwise problematic, and the FOR EACH call has errors.
 
lbell
Topic Author
Posts: 28
Joined: Wed Sep 03, 2014 7:36 pm

Re: Trouble with parsing

Fri Sep 12, 2014 2:54 pm

I didn't even load the sd xml feeds online yet. Is that part of my problem?
 
User avatar
RokuKC
Posts: 315
Joined: Wed Sep 10, 2014 10:44 am
Location: Roku HQ

Re: Trouble with parsing

Fri Sep 12, 2014 5:07 pm

Hi lbell,

In categoriesfeed.xml, it looks like there are elements like:
    <categoryLeaf title="Video sermon categoryleaf 1" description="" feed="http://www.map2life.com/roku/xml/videosermonfeed.xml"/>


ParseCategoryNode is being passed that element via recursive calls and does:
    for each e in xml.GetBody()


GetBody returns invalid for the above categoryLeaf XML, because it does not have any child text or child elements.

So the 'for each' is being run on an 'invalid' list.

The diagnostic message:
   BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid

was added in the firmware to help developers catch errors, as it is assumed iterating on invalid was not intentional.

Although 'for each' is documented as taking an enumerable object, for an 'invalid' parameter it is just a warning rather than a runtime error.

Hope that helps.
 
EnTerr
** Valued Community Member **
Posts: 3834
Joined: Sun Jan 02, 2011 2:41 am

Re: Trouble with parsing

Sat Sep 13, 2014 1:03 am

RokuKC wrote:
The diagnostic message:
   BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is Invalid

was added in the firmware to help developers catch errors, as it is assumed iterating on invalid was not intentional.
Although 'for each' is documented as taking an enumerable object, for an 'invalid' parameter it is just a warning rather than a runtime error.

Hi RokuKC, welcome -
so this message is a newcomer?
No wonder when i saw it in this question i thought no way the interpreter issued that: there is no line number and the output text is, umm, sub-standard (3 colons? all-caps?). I just checked and it does not interrupt the program not only for invalid but for other non-enums either:
BrightScript Debugger> for each i in "boza": ? i: end for
BRIGHTSCRIPT: ERROR: Runtime: FOR EACH value is not an object

Can you fix it? For comparison, here is what i consider reasonable behavior:
BrightScript Debugger> for i = "1" to "2": ? i: end for
Attempt to use a non-numeric array index not allowed. (runtime error &he8) in $LIVECOMPILE(1179)
- because there is a line number displayed so issue can be located right away; and it is a proper error and not just a warning.
 
User avatar
TheEndless
** Valued Community Member **
Posts: 9231
Joined: Mon Oct 04, 2004 10:15 am
Location: US
Contact:

Re: Trouble with parsing

Sat Sep 13, 2014 1:44 am

Agreed.. while the error/warning is helpful to an extent.. it's pretty useless without a line number to help pinpoint where the issue is.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
 
User avatar
RokuKC
Posts: 315
Joined: Wed Sep 10, 2014 10:44 am
Location: Roku HQ

Re: Trouble with parsing

Sat Sep 13, 2014 10:14 am

Yes, I agree that having even minimal source context will make non-fatal error messages from script runs much more useful. :)

It's on the backlog, but not as easy as you might think. :(
 
EnTerr
** Valued Community Member **
Posts: 3834
Joined: Sun Jan 02, 2011 2:41 am

Re: Trouble with parsing

Sat Sep 13, 2014 12:51 pm

RokuKC wrote:
Yes, I agree that having even minimal source context will make non-fatal error messages from script runs much more useful. :)
It's on the backlog, but not as easy as you might think. :(

Ehehe, I am not as easy to think as one may think :D
First I remembered of another warning, which too is pretty useless without context. Oh, the extra effort it took me to track down. I did guess then line# might not be easily avail but did suggest another piece of info that is on hand.

But the example i showed here clearly should be an error - an interrupting, runtime error. And so should be
for each app in createObject("roChannelStore"): ? app: end for
- basically anytime when non-ifEnum-erable object is passed to `for each`, it should stop with a stack trace, just like in the case of `for i="1" to "2"`. At that point it is clear something have gone wrong beyond reason. That's easy to do, right?
 
User avatar
RokuKC
Posts: 315
Joined: Wed Sep 10, 2014 10:44 am
Location: Roku HQ

Re: Trouble with parsing

Thu Sep 18, 2014 3:11 pm

EnTerr wrote:
First I remembered of another warning, which too is pretty useless without context. Oh, the extra effort it took me to track down. I did guess then line# might not be easily avail but did suggest another piece of info that is on hand.


Yes, I've hit this too, and agree that the invalid paths messages should at minimum include the path in question as clue.

EnTerr wrote:
But the example i showed here clearly should be an error - an interrupting, runtime error. And so should be
for each app in createObject("roChannelStore"): ? app: end for
- basically anytime when non-ifEnum-erable object is passed to `for each`, it should stop with a stack trace, just like in the case of `for i="1" to "2"`. At that point it is clear something have gone wrong beyond reason. That's easy to do, right?


Turning what is currently a non-fatal error into an fatal error in a platform update is not something I would advocate without very good reason, as it could easily break functional apps.
I could certainly argue that it should have been a fatal error in the first place, but it is what it is.

That doesn't preclude adding a 'strict' mode in the future, that apps could opt in to, or could use when side loading that would turn it into a fatal error to better support diagnostics. But there are still going to be differences of opinion vs. which diagnostics should be fatal vs. just warnings.

Who is online

Users browsing this forum: No registered users and 10 guests