Search code examples
xmldtdxml-validation

XML/DTD: Validation error for attributes


I'd really appreciate your help with this xml-code. I am quite a newbie to all of this...so thank you so much in advance!! Here it goes: For my class I wrote a recipe as xml and then included the dtd:

`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE recipe
 [
 <!ENTITY writer "Matt Tebutt">
 <!ENTITY copyright "BBC">
 <!ELEMENT recipe (title,preptime,cooktime,serves,comment,ingredients,(burgers,(listitem*,(name,quantity)*),salad,(listitem*,(name,quantity)*),toserve,(listitem*,(name,quantity)*)),method,(step)*)>
 <!ELEMENT title (#PCDATA)>
 <!ATTLIST title xml:lang NMTOKEN #FIXED "en">
 <!ELEMENT preptime (#PCDATA)>
 <!ELEMENT cooktime (#PCDATA)>
 <!ELEMENT serves (#PCDATA)>
 <!ELEMENT comment (#PCDATA)>
 <!ELEMENT ingredients (#PCDATA)>
 <!ELEMENT burgers (#PCDATA)>
 <!ELEMENT listitem (#PCDATA)>
 <!ELEMENT name (#PCDATA)>
 <!ELEMENT quantity (#PCDATA)>
 <!ATTLIST quantity unit (tablespoon|clove|pinch|bunch|gram) #IMPLIED>
 <!ELEMENT salad (#PCDATA)>
 <!ELEMENT toserve (#PCDATA)>
 <!ELEMENT method (#PCDATA)>
 <!ELEMENT step (#PCDATA)>
 ]>
 <recipe>
    <title xml:lang="en">Chickpea burgers</title>
    <preptime>&lt;30 min</preptime>
    <cooktime>10 to 30 min</cooktime>
    <serves>serves 4</serves>
    <comment>
        These are delicious vegan burgers with a chilli kick. They can be prepared in advance and left in the fridge until you're ready to cook.
    </comment>
    <ingredients>
        <burgers>
            <listitem>
                <name>olive oil</name>
                <quantity unit="tablespoon">2</quantity>
            </listitem>
            <listitem>
                <name>red onions, diced</name>
                <quantity>2</quantity>
            </listitem>
            <listitem>
                <name>garlic, finely chopped</name>
                <quantity unit="clove">3</quantity>
            </listitem>
            <listitem>
                <name>ground coriander</name>
                <quantity unit="tablespoon">1</quantity>
            </listitem>
            <listitem> 
                <name>ground cumin</name>
                <quantity unit="tablespoon">0.5</quantity>
            </listitem>
            <listitem>
                <name>ground turmeric</name>
                <quantity unit="pinch">1</quantity>
            </listitem>
            <listitem>
                <name>hot red chilli, finely chopped</name>
                <quantity>1</quantity>
            </listitem>
            <listitem>
                <name>fresh coriander and the stalks, finely chopped</name>
                <quantity unit="bunch">1</quantity>
            </listitem>
            <listitem>
                <name>cooked chickpeas, drained</name>
                <quantity unit="gram">2x400</quantity>
            </listitem>
            <listitem>
                <name>lemon, juice only</name>
                <quantity> 1</quantity>
            </listitem>
            <listitem>
                <name>ground polenta</name>
                <quantity unit="gram">250</quantity>
            </listitem>
            <listitem>
                <name>salt</name>
            </listitem>
            <listitem>
                <name>freshly ground pepper</name>
            </listitem>
        </burgers>
        <salad>
            <listitem>
                <name>red onion, sliced</name>
                <quantity>0.5</quantity>
            </listitem>
            <listitem>
                <name>red pepper, sliced</name>
                <quantity>0.5</quantity>
            </listitem>
            <listitem>
                <name>salt</name>
                <quantity>to taste</quantity>
            </listitem>
            <listitem>
                <name>ground sumac"</name>
                <quantity unit="pinch">1</quantity>
            </listitem>
            <listitem>
                <name>tomatoes, sliced</name>
                <quantity>2</quantity>
            </listitem>
            <listitem>
                <name>large handful Cos lettuce leaves</name>
                <quantity>1</quantity>
            </listitem>
        </salad>
        <toserve>
            <listitem>
                <name>Sriracha chilli sauce</name>
                <quantity>to taste</quantity>
            </listitem>
            <listitem>
                <name>vegan mayonnaise</name>
                <quantity>to taste</quantity>
            </listitem>
            <listitem>
                <name>burger buns</name>
                <quantity>4</quantity>
            </listitem>
        </toserve>
    </ingredients>
    <method>
        <step>
            1. Heat the olive oil in a pan and fry the onion and garlic for ten minutes until soft. Add the spices and coriander and cook for another minute. Remove from the heat.
        </step>
        <step>
            2. Pulse the chickpeas in a blender with the onion spice mix. Taste, season with salt and pepper, and add a squeeze of lemon juice. Form the mixture into stiff patties and set aside in the fridge until you are ready to cook.
        </step>
        <step>
            3. For the salad, salt the red onion and red pepper on a plate and leave for 20 minutes.
        </step>
        <step>
            4. When ready to cook the burgers, sprinkle the polenta onto a plate and press the burgers onto the polenta. Heat the sunflower oil in a pan and fry the burgers on both sides until cooked through. (Alternatively, you can bake these on an oiled tray for 15-20 minutes at 200C/180C Fan/Gas 6.)
        </step>
        <step>
            5. To finish the salad, wash the onion and pepper under cold, running water and drain. Sprinkle the sumac over the tomatoes. Combine the tomatoes with the Cos lettuce leaves, red onion and red pepper. Mix together the chilli sauce and mayonnaise in a small dish.
        </step>
        <step>
            6. Serve the burgers in the buns with the salad and chilli dip alongside.
        </step>
    </method>
 </recipe>`

However I can't get the attribute declaration for unit right. When I try to validate, I get a whole list of errors of this:

The content of element type "listitem" must match "null".

If you could help me please?!

Thank you!!!


Solution

  • The problem isn't with the attribute declaration. It's that you have listitem declared as containing #PCDATA when it looks like it should be declared (name, quantity).

    There are other elements declared incorrectly like ingredients and burgers.

    I think the problems all start with the declaration for recipe.

    To get your XML to validate as-is, change your doctype declaration to this:

    <!DOCTYPE recipe [
    <!ENTITY writer "Matt Tebutt">
    <!ENTITY copyright "BBC">
    <!ELEMENT recipe (title,preptime,cooktime,serves,comment,ingredients,method)>
    <!ELEMENT title (#PCDATA)>
    <!ATTLIST title xml:lang NMTOKEN #FIXED "en">
    <!ELEMENT preptime (#PCDATA)>
    <!ELEMENT cooktime (#PCDATA)>
    <!ELEMENT serves (#PCDATA)>
    <!ELEMENT comment (#PCDATA)>
    <!ELEMENT ingredients (burgers?,salad?,toserve?)>
    <!ELEMENT burgers (listitem*)>
    <!ELEMENT listitem (name,quantity?)>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENT quantity (#PCDATA)>
    <!ATTLIST quantity unit (tablespoon|clove|pinch|bunch|gram) #IMPLIED>
    <!ELEMENT salad (listitem*)>
    <!ELEMENT toserve (listitem*)>
    <!ELEMENT method (step*)>
    <!ELEMENT step (#PCDATA)>
    ]>
    

    A couple of comments though.

    There are really two recipes in your document; one for burgers and one for salads. I think it would make more sense for these to be their own individual recipe's. This could either be in separate documents or you could change your root element to recipes and have multiple recipe children.

    Having element names like burger and salad make it much harder to extend in the future. If you really want to capture that info, I'd add a type attribute or something to recipe (not shown in example).

    Numbering the steps in the content shouldn't be necessary. The "1." in <step>1. Heat the olive oil...</step> is implied since that step is the first child of method. (Similar to li in ol in HTML.)

    It might be good to add a unit attribute to preptime and cooktime and leave the values just a number/number range. That way whatever consumes your XML can more easily use the data; for instance calculating a total time (not shown in example). The same thing goes for serves; leave that value as just a number.

    Example

    <!DOCTYPE recipes [
    <!ENTITY writer "Matt Tebutt">
    <!ENTITY copyright "BBC">
    <!ELEMENT recipes (recipe+)>
    <!ELEMENT recipe (title,preptime,cooktime,serves,comment?,ingredients,method)>
    <!ELEMENT title (#PCDATA)>
    <!ATTLIST title xml:lang NMTOKEN #FIXED "en">
    <!ELEMENT preptime (#PCDATA)>
    <!ELEMENT cooktime (#PCDATA)>
    <!ELEMENT serves (#PCDATA)>
    <!ELEMENT comment (#PCDATA)>
    <!ELEMENT ingredients (listitem+,toserve?)>
    <!ELEMENT listitem (name,quantity?)>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENT quantity (#PCDATA)>
    <!ATTLIST quantity unit (tablespoon|clove|pinch|bunch|gram) #IMPLIED>
    <!ELEMENT toserve (listitem*)>
    <!ELEMENT method (step*)>
    <!ELEMENT step (#PCDATA)>
    ]>
    <recipes>
        <recipe>
            <title xml:lang="en">Chickpea burgers</title>
            <preptime>&lt;30 min</preptime>
            <cooktime>10 to 30 min</cooktime>
            <serves>4</serves>
            <comment>These are delicious vegan burgers with a chilli kick. They can be prepared in
                advance and left in the fridge until you're ready to cook.</comment>
            <ingredients>
                <listitem>
                    <name>olive oil</name>
                    <quantity unit="tablespoon">2</quantity>
                </listitem>
                <listitem>
                    <name>red onions, diced</name>
                    <quantity>2</quantity>
                </listitem>
                <listitem>
                    <name>garlic, finely chopped</name>
                    <quantity unit="clove">3</quantity>
                </listitem>
                <listitem>
                    <name>ground coriander</name>
                    <quantity unit="tablespoon">1</quantity>
                </listitem>
                <listitem>
                    <name>ground cumin</name>
                    <quantity unit="tablespoon">0.5</quantity>
                </listitem>
                <listitem>
                    <name>ground turmeric</name>
                    <quantity unit="pinch">1</quantity>
                </listitem>
                <listitem>
                    <name>hot red chilli, finely chopped</name>
                    <quantity>1</quantity>
                </listitem>
                <listitem>
                    <name>fresh coriander and the stalks, finely chopped</name>
                    <quantity unit="bunch">1</quantity>
                </listitem>
                <listitem>
                    <name>cooked chickpeas, drained</name>
                    <quantity unit="gram">2x400</quantity>
                </listitem>
                <listitem>
                    <name>lemon, juice only</name>
                    <quantity> 1</quantity>
                </listitem>
                <listitem>
                    <name>ground polenta</name>
                    <quantity unit="gram">250</quantity>
                </listitem>
                <listitem>
                    <name>salt</name>
                </listitem>
                <listitem>
                    <name>freshly ground pepper</name>
                </listitem>
                <toserve>
                    <listitem>
                        <name>Sriracha chilli sauce</name>
                        <quantity>to taste</quantity>
                    </listitem>
                    <listitem>
                        <name>vegan mayonnaise</name>
                        <quantity>to taste</quantity>
                    </listitem>
                    <listitem>
                        <name>burger buns</name>
                        <quantity>4</quantity>
                    </listitem>
                </toserve>
            </ingredients>
            <method>
                <step>Heat the olive oil in a pan and fry the onion and garlic for ten minutes until
                    soft. Add the spices and coriander and cook for another minute. Remove from the
                    heat.</step>
                <step>Pulse the chickpeas in a blender with the onion spice mix. Taste, season with salt
                    and pepper, and add a squeeze of lemon juice. Form the mixture into stiff patties
                    and set aside in the fridge until you are ready to cook.</step>
                <step>For the salad, salt the red onion and red pepper on a plate and leave for 20
                    minutes.</step>
                <step>When ready to cook the burgers, sprinkle the polenta onto a plate and press the
                    burgers onto the polenta. Heat the sunflower oil in a pan and fry the burgers on
                    both sides until cooked through. (Alternatively, you can bake these on an oiled tray
                    for 15-20 minutes at 200C/180C Fan/Gas 6.)</step>
                <step>Mix together the chilli sauce and mayonnaise in a small dish.</step>
                <step>Serve the burgers in the buns with the chilli dip alongside.</step>
            </method>
        </recipe>
        <recipe>
            <title>Salad</title>
            <preptime>10 min</preptime>
            <cooktime>0 min</cooktime>
            <serves>4</serves>
            <ingredients>
                <listitem>
                    <name>red onion, sliced</name>
                    <quantity>0.5</quantity>
                </listitem>
                <listitem>
                    <name>red pepper, sliced</name>
                    <quantity>0.5</quantity>
                </listitem>
                <listitem>
                    <name>salt</name>
                    <quantity>to taste</quantity>
                </listitem>
                <listitem>
                    <name>ground sumac</name>
                    <quantity unit="pinch">1</quantity>
                </listitem>
                <listitem>
                    <name>tomatoes, sliced</name>
                    <quantity>2</quantity>
                </listitem>
                <listitem>
                    <name>large handful Cos lettuce leaves</name>
                    <quantity>1</quantity>
                </listitem>
            </ingredients>
            <method>
                <step>Wash the onion and pepper under cold, running water and drain.</step>
                <step>Sprinkle the sumac over the tomatoes.</step>
                <step>Combine the tomatoes with the Cos lettuce leaves, red onion and red pepper.</step>
            </method>
        </recipe>
    </recipes>