You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by bu...@apache.org on 2005/07/13 10:07:26 UTC

DO NOT REPLY [Bug 35714] New: - [betwixt] POJO->XML . 'null' reference sin arrays or collection not generated in XML

DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG�
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://issues.apache.org/bugzilla/show_bug.cgi?id=35714>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND�
INSERTED IN THE BUG DATABASE.

http://issues.apache.org/bugzilla/show_bug.cgi?id=35714

           Summary: [betwixt] POJO->XML .  'null' reference sin arrays or
                    collection not generated in XML
           Product: Commons
           Version: unspecified
          Platform: Other
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Betwixt
        AssignedTo: commons-dev@jakarta.apache.org
        ReportedBy: temporaryaddress@skynet.be


Hi,

I apologise if this is a frequently asked question but I didn't see it answered
in the FAQ and 10 minutes of searching didn't show anything.

My problem is that I want to be sometimes able to store the fact that for a
particular class field there is NO value.

Simplified to the extreme: the problem is that I want to present a web user with
a list of prompts (descriptions) and the user should type in values or nothing
depending on whether or not a value is appropriate or not.

I want to store the whole thing as name:value pairs and the order IS important
and must be preserved.

So I have several options on how to implement this.
for example:
1) Using two arrays in parallel - one for the questions and another for the answers.
or
2) Using LinkedHashMap 
or 
3) Defining a special place-holder object to represent "Don't know / No comment".

I guess I am going to have to use approach 3 unless someone has got an idea of
how to make one of the other approaches work.  Here is what I tried for 1&2

1) Using two arrays in parallel
-------------------------------
I tried declaring an array of Objects (for the moment only holding Strings) for
the descriptions and a parallel array of Objects for the values.
    private Object [] descriptions;
    private Object [] values; 
Using this approach Xstream behaves exactly like I want in this respect - it
generated a special <null/> tag for array elements containing null values (in
this example the first 3 elements of the "values" array contained null references).

              <descriptions class="string-array">
                   <string>London-Brussels</string>
                   <string>Brussels-London</string>
                   <string>Air</string>
                   <string>Cost per person</string>
                   <string>Nr. Persons</string>
              </descriptions>
              <values>
                   <null/>
                   <null/>
                   <null/>
                   <double>132.11</double>
                   <int>3</int>
              </values>

This conveys the information to my users that  a flight London-Brussels return
should cost Euro 132.11 per person and that 3 people will be travelling.

Trying the same thing with betwixt gave me something like this ...

              <descriptions class="string-array">
                   <string>London-Brussels</string>
                   <string>Brussels-London</string>
                   <string>Air</string>
                   <string>Cost per person</string>
                   <string>Nr. Persons</string>
              </descriptions>
              <values>                                             
                   <double>132.11</double>
                   <int>3</int>
              </values>

So there is not output for the null values and this destroys the association
between the two arrays.


2) Using LinkedHashMap 
----------------------

The problem here was that betwixt generated ID references to elements that it
never defines.
I have listed an abbreviated the code of the class below which contains some
test-code in 
the constructor.
The full output is listed below (the output is the result from the test code in
the constructor and other test-code in a JUnit testcase which I wrote to test
the original parallel array solution).
The program serializes a whole tree of objects so the first item that directly
illustrates
the problem is the element with id="5".
Here there seems to be 3 elements for the entries in the LinkedHashMap but the
actual data does not seems to be stored anywhere.  Other elements reference
these elements (which is reasonable since 
these are constant) but the actual data I wanted to store is not there.



public class BudgetLeafItemImpl extends BaseBudgetItemImpl implements
BudgetLeafItem {

    private LinkedHashMap testList = new LinkedHashMap();
    private Object [] descriptions;
    private Object [] values; 
    
    //...
    public BudgetLeafItemImpl() {
        super();
        testList.put("Desc1", null);
        testList.put("Desc2",null);
        testList.put("Desc3", new Double(123.2));    }

    // .. get/setters, business logic omitted
}


<?xml version="1.0"?>  
<budgetNonLeafItemImpl leaf-item-type="false" mandatory="false" title="A1
Eligible costs" valid="true" id="1">
    <children>
      <child leaf-item-type="false" mandatory="true" title="A1.1 Personnel"
valid="true" id="2">
        <children/>
        <setterInterface/>
        <setterInterfaceNonLeaf/>
      </child>
      <child leaf-item-type="false" mandatory="false" title="A1.2 Travel and
accomodation" valid="true" id="3">
        <children>
          <child leaf-item-type="false" mandatory="false" title="A1.2.1 Travel"
valid="true" id="4">
            <children>
              <child leaf-item-type="true" mandatory="false" title="A1.2.1.1
Travel - London Brussels" valid="true" id="5">
                <descriptions>
                  <string>London-Brussels</string>
                  <string>Brussels-London</string>
                  <string>Air</string>
                  <string>Cost per person</string>
                  <string>Nr. Persons</string>
                </descriptions>
                <setterInterface/>
                <testList>
                  <entry id="6"/>
                  <entry id="7"/>
                  <entry id="8"/>
                </testList>
                <values>
                  <double>132.11</double>
                  <integer>3</integer>
                </values>
              </child>
              <child leaf-item-type="true" mandatory="false" title="A1.2.1.2
Travel - Amsterdam Brussels" valid="true" id="9">
                <descriptions>
                  <string>Brussels-Amsterdam</string>
                  <string>Amsterdam-Brussels</string>
                  <string>Train</string>
                  <string>Cost per person</string>
                  <string>Nr. Persons</string>
                </descriptions>
                <setterInterface/>
                <testList>
                  <entry idref="6"/>
                  <entry idref="7"/>
                  <entry idref="8"/>
                </testList>
                <values>
                  <double>100.0</double>
                  <integer>1</integer>
                </values>
              </child>
            </children>
            <setterInterface/>
            <setterInterfaceNonLeaf/>
          </child>
        </children>
        <setterInterface/>
        <setterInterfaceNonLeaf/>
      </child>
      <child leaf-item-type="false" mandatory="false" title="A1.3 Implementation
costs" valid="true" id="10">
        <children>
          <child leaf-item-type="false" mandatory="false" title="A1.3.1
Implementation costs - Translations" valid="true" id="11">
            <children>
              <child leaf-item-type="true" mandatory="false" title="A1.3.1.1
Translation of questionaire for all member states" valid="true" id="12">
                <descriptions>
                  <string>English</string>
                  <string>French</string>
                  <string>Cost per page (Euro)</string>
                  <string>Nr. pages</string>
                </descriptions>
                <setterInterface/>
                <testList>
                  <entry idref="6"/>
                  <entry idref="7"/>
                  <entry idref="8"/>
                </testList>
                <values>
                  <double>10.5</double>
                  <integer>10</integer>
                </values>
              </child>
              <child leaf-item-type="true" mandatory="false" title="A1.3.1.1
Translation of questionaire for all member states" valid="true" id="13">
                <descriptions>
                  <string>English</string>
                  <string>French</string>
                  <string>Cost per page (Euro)</string>
                  <string>Nr. pages</string>
                </descriptions>
                <setterInterface/>
                <testList>
                  <entry idref="6"/>
                  <entry idref="7"/>
                  <entry idref="8"/>
                </testList>
                <values>
                  <double>10.5</double>
                  <integer>10</integer>
                </values>
              </child>
            </children>
            <setterInterface/>
            <setterInterfaceNonLeaf/>
          </child>
        </children>
        <setterInterface/>
        <setterInterfaceNonLeaf/>
      </child>
      <child leaf-item-type="false" mandatory="false" title="A1.4 Frais
r�sultant d&apos;exigences de la convention" valid="true" id="14">
        <children>
          <child leaf-item-type="true" mandatory="false" title="A1.4.1 Audit
reports" valid="true" id="15">
            <descriptions>
              <string>Audit fees</string>
            </descriptions>
            <setterInterface/>
            <testList>
              <entry idref="6"/>
              <entry idref="7"/>
              <entry idref="8"/>
            </testList>
            <values>
              <double>0.0</double>
            </values>
          </child>
          <child leaf-item-type="true" mandatory="false" title="A1.4.2 Financial
guarantee" valid="true" id="16">
            <descriptions>
              <string>Cost of obtaining financial guarantee</string>
            </descriptions>
            <setterInterface/>
            <testList>
              <entry idref="6"/>
              <entry idref="7"/>
              <entry idref="8"/>
            </testList>
            <values>
              <double>0.0</double>
            </values>
          </child>
        </children>
        <setterInterface/>
        <setterInterfaceNonLeaf/>
      </child>
      <child leaf-item-type="false" mandatory="false" title="A1.7 Indirect and
other costs (max 7% of eligible costs)" valid="true" id="17">
        <children>
          <child leaf-item-type="true" mandatory="false" title="A1.7.1 General
expenses" valid="true" id="18">
            <descriptions>
              <string>General expenses</string>
            </descriptions>
            <setterInterface/>
            <testList>
              <entry idref="6"/>
              <entry idref="7"/>
              <entry idref="8"/>
            </testList>
            <values>
              <double>0.0</double>
            </values>
          </child>
        </children>
        <setterInterface/>
        <setterInterfaceNonLeaf/>
      </child>
    </children>
    <setterInterface/>
    <setterInterfaceNonLeaf/>
  </budgetNonLeafItemImpl>

The code to generate the XML (a different class) follows: 
It accepts a budget tree which is a Non-Leaf object (header) which contains a
collection of other headers or BudgetLeafItemImpl which represent actual items
with costs in the budget.
class Lister {  ...
    private String toXML(BudgetNonLeafItem budgTree) {

        StringWriter xmlOut = new StringWriter();
        BeanWriter writer = new BeanWriter(xmlOut);
        IntrospectionConfiguration cfg =
writer.getXMLIntrospector().getConfiguration(); 
        cfg.setAttributesForPrimitives(true);
        writer.enablePrettyPrint();
        writer.getBindingConfiguration().setMapIDs(true); 
        
        // set a custom name mapper for attributes
        cfg.setAttributeNameMapper(new HyphenatedNameMapper());
        // set a custom name mapper for elements
        cfg.setElementNameMapper(new DecapitalizeNameMapper());
        writer.setWriteEmptyElements(true);
        
        // write out the bean
        String xmlString = null;
        try {
            writer.write(budgTree);
            xmlString = xmlOut.toString(); 
            writer.close();
        } 
        catch (Throwable t) { // theoretically possible but unlikely
            t.printStackTrace(System.err);
            System.err.println("Error converting budget to xml:" + budgTree);
        }
        return xmlString; 
    }
}


Thanks,
Richard Sullivan

ps Sorry about the obviously anonymous mail address but I don't want even more
spam than I already have thanks !

-- 
Configure bugmail: http://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org