Templates and xml

Jan 26, 2012 at 4:42 AM

First, Atul, congratulations and thanks for this utility!

I would like to know if it is possible generate a document with only a xml and a template without a schema (xsd) and Xpath (ControlTagXPath and ControlValueXPath).

The intention is a generic code without the need to change my code for a new template.

Best regards,

Jorge

Coordinator
Jan 26, 2012 at 8:56 AM

Thanks Jorge. Schema can be ignored. I added schema just to validate the XML. Using XPath

1. For databound content controls we need to specify the XPath

2. Using XPath I can fetch value from XML and then use that to set the control tag or control value

Without specifying XPath I don't think there is a way where we can map the XML to Content Control automatically. Thanks.

Jan 26, 2012 at 5:28 PM
Edited Jan 26, 2012 at 5:36 PM

Atul, the xml could have only two tags <listFields> and <field>.  Example: for "Test_Template - 1.docx", the class Program.cs has a xml like this: 

<Order>
  <Id>736d79d9-0689-419e-b27a-1867f613fd80</Id>
  <Name>Test Order</Name>
  <Description>Test Order Description</Description>
  <vendors>
    <Vendor>
      <Id>469c8927-6a68-4f16-b267-acaf38fc2d39</Id>
      <Name>Vendor 1</Name>
    </Vendor>
    <Vendor>
      <Id>48e1f6d0-5060-4725-ae45-51e81b2e89d6</Id>
      <Name>Vendor 2</Name>
    </Vendor>
    ...
  </vendors>
  <items>
    <Item>
      <Id>81474c98-0094-499d-88d7-678f40581b50</Id>
      <Name>Item 1</Name>
    </Item>
    <Item>
      <Id>60a5d7bc-304a-49fa-be4a-684f91adf6c5</Id>
      <Name>Item 2</Name>
    </Item>
    ...
  </items>
</Order>
 
But, it could be a xml like this:
<listFields>
  <field type="2" tag="orderName" value="Test Order" />
  <field type="2" tag="orderDescription" value="Test Order Description" />
  <field type="1" tag="vendors"> 
    <listFields>
      <field type="2" tag="vendorName" value="Vendor 1" />
    </listFields>
    <listFields>
      <field type="2" tag="vendorName" value="Vendor 2" />
    </listFields>
  </field>
  <field type="1" tag="items"> 
    <listFields>
      <field type="2" tag="itemName" value="Item 1" />
    </listFields>
    <listFields>
      <field type="2" tag="itemName" value="Item 2" />
    </listFields>
  </field>
</listFields>

The types are the same as PlaceHolderType enum:

public enum PlaceHolderType {
        None = 0,
        Recursive = 1,
        NonRecursive = 2,
        Ignore = 3,
        Container = 4
    }

 

If you prefer maintain the tag names of content controls that are in "Test_Template - 1.docx",

your xml could it be:

<listFields>
  <field type="2" tag="PlaceholderNonRecursiveC" value="Test Order" />
  <field type="2" tag="PlaceholderNonRecursiveD" value="Test Order Description" />
  <field type="1" tag="PlaceholderRecursiveA"> 
    <listFields>
      <field type="2" tag="PlaceholderNonRecursiveA" value="Vendor 1" />
    </listFields>
    <listFields>
      <field type="2" tag="PlaceholderNonRecursiveA" value="Vendor 2" />
    </listFields>
  </field>
  <field type="1" tag="PlaceholderRecursiveB"> 
    <listFields>
      <field type="2" tag="PlaceholderNonRecursiveB" value="Item 1" />
    </listFields>
    <listFields>
      <field type="2" tag="PlaceholderNonRecursiveB" value="Item 2" />
    </listFields>
  </field>
</listFields>


Thus, I would like to know if it possible to write a generic code for a generic xml like

this above.

Thanks a lot,

Jorge

Coordinator
Jan 26, 2012 at 6:48 PM

Thanks Jorge. The approach suggested by you should work. I'll try to incorporate it soon in next update which I have planned in another 10-15 days.

Jan 26, 2012 at 7:18 PM
Edited Jan 26, 2012 at 7:20 PM

Thanks for your reply, Atul. I look forward to the next update of WordDocumentGenerator.

Coordinator
Jan 30, 2012 at 6:54 PM

Jorge the XML structure I have finalized is displayed below

- <field>
  - <listFields>
    <field orderName="Test Order" orderDescription="Test Order Description" tagValue="" contentControlTagREFS="PlaceholderNonRecursiveC PlaceholderNonRecursiveD"/>
    <field tagValue="736d79d9-0689-419e-b27a-1867f613fd80" contentControlTagREFS="PlaceholderContainerA">
      <listFields>
        - <field tagValue="736d79d9-0689-419e-b17a-1867f613fd80" contentControlTagREFS="PlaceholderRecursiveA">
          - <listFields>
            <field vendorName="Vendor 1" tagValue="736d79d9-1689-419e-c27a-1867f613fd80" contentControlTagREFS="PlaceholderNonRecursiveA"/>
            <field vendorName="Vendor 2" tagValue="736d79d9-0589-419e-c27a-1867f613fd80" contentControlTagREFS="PlaceholderNonRecursiveA"/>
          </listFields>
        </field>
        - <field tagValue="736d79d9-0689-119e-b27a-1867f613fd80" contentControlTagREFS="PlaceholderRecursiveB">
          - <listFields>
            <field itemName="Item 1" tagValue="736d79d9-0689-419e-c27a-1867f613fd80" contentControlTagREFS="PlaceholderNonRecursiveB" />
            <field itemName="Item 2" tagValue="736d79d9-0689-419e-b27a-1867f613dd80" contentControlTagREFS="PlaceholderNonRecursiveB" />
          </listFields>
        </field>
      </listFields>
    </field>
  </listFields>
  <contentControls>
    <contentControl type="2" tag="PlaceholderNonRecursiveC" attrToPick="orderName"/>
    <contentControl type="2" tag="PlaceholderNonRecursiveD" attrToPick="orderDescription"/>
    <contentControl type="4" tag="PlaceholderContainerA" />
    <contentControl type="1" tag="PlaceholderRecursiveA" />
    <contentControl type="1" tag="PlaceholderRecursiveB" />
    <contentControl type="2" tag="PlaceholderNonRecursiveA" attrToPick="vendorName" />
    <contentControl type="2" tag="PlaceholderNonRecursiveB" attrToPick="itemName"/>
  </contentControls>
</field>

Here <field> is the root node and contains two child nodes i.e. <listFields> and <contentControls>.

<contentControls>: This node contains information about the content controls of the Word template. Each content control element i.e. <contentControl> can have three attributes

  • type: This can be recursive, non recursive, container etc. as defined in PlaceHolder enum
  • tag: Content control tag as defined in Word template
  • attrToPick(Optional): This is applicable only in case of non recursive controls. It specifies the name of the attribute whose value should be assigned to this content control

<listFields>: This node contains the data required to populate the Word template. It will have <field> as child nodes. <field> can have

  • Attributes: Attributes will be
    • tagValue(Optional): Tag value to append to content control e.g. if Tag is A, text is "Test Order" and tagValue is "123" we update control's tag to "A:123". This will help me in cases e.g. Updating database from Word document. Then I can split the tagValue and get the record id. Thus if sich cases are not intended this is optional.
    • contentControlTagREFS: One or more content controls can be populated from a field element. Hence this tag specifies the content control tags. Each tag is separated by space.
    • Other attributes that define data e.g. itemName, vendorName, orderName, orderDescription etc. The attribute is mapped to content control through attrToPick attribute
  • <listFields> as Child Nodes: These are there to group related records e.g. List of vendors or list of items.

Please share your feedback so that I can work towards making this better and easier. Thanks.

Atul

Jan 31, 2012 at 12:15 AM

Atul, it´s a very good solution. It is more flexible. Some suggestions:
1) When tag name and content control name are equals, the attributes "contentControlTagREFS" and "attrToPick" could be optional. The xml would be:

<field>
  <listFields>
    <field orderName="Test Order" orderDescription="Test Order Description"/>
    <field tagValue="736d79d9-0689-419e-b27a-1867f613fd80" contentControlTagREFS="PlaceholderContainerA">
      <listFields>
        <field tagValue="736d79d9-0689-419e-b17a-1867f613fd80" contentControlTagREFS="PlaceholderRecursiveA">
          <listFields>
            <field vendorName="Vendor 1" tagValue="736d79d9-1689-419e-c27a-1867f613fd80"/>
            <field vendorName="Vendor 2" tagValue="736d79d9-0589-419e-c27a-1867f613fd80"/>
          </listFields>
        </field>
        <field tagValue="736d79d9-0689-119e-b27a-1867f613fd80" contentControlTagREFS="PlaceholderRecursiveB">
          <listFields>
            <field itemName="Item 1" tagValue="736d79d9-0689-419e-c27a-1867f613fd80"/>
            <field itemName="Item 2" tagValue="736d79d9-0689-419e-b27a-1867f613dd80"/>
          </listFields>
        </field>
      </listFields>
    </field>
  </listFields>
  <contentControls>
    <contentControl type="2" tag="orderName"/>
    <contentControl type="2" tag="orderDescription"/>
    <contentControl type="4" tag="PlaceholderContainerA" />
    <contentControl type="1" tag="PlaceholderRecursiveA" />
    <contentControl type="1" tag="PlaceholderRecursiveB" />
    <contentControl type="2" tag="vendorName"/>
    <contentControl type="2" tag="itemName"/>
  </contentControls>
</field>

2)  About "tagValue" you said "This will help me in cases e.g. Updating database from Word document." Some questions:

2.1) Is there any example doing this? It's very interesting.

2.2) If you specify the table name in tag value, the mapping between xml and database table could be more generic. e.g.  

tagValue="ClientTable: 736d79d9-0689-419e-c27a-1867f613fd80"

3) Is it mandatory a table be inside a container? Do you need to distinguish if a recursive type is a table? If you need, besides the type Recursive = 1, could have a new type RecursiveInTable = 5.

Thanks!

Jorge

 

Coordinator
Jan 31, 2012 at 10:00 AM

Thanks for your feedback Jorge. My thoughts are

1. "contentControlTagREFS" attribute will be a mandatory attribute. e.g. for this case 

<contentControl type="2" tag="orderName"/>

"orderName" attribute may be present in multiple elements(lets say a new field SecondaryOrder). Then in that case this will cause an issue. Hence I think contentControlTagREFS needs to be mandatory.

2. On the same lines as discussed above I would choose to keep "attrToPick" attribute. As per your approach content control tag will be equal to the attribute name. This makes things easy however multiple elements(lets say a new field SecondaryOrder) can have same attribute names and then this will fail.

3. "tagValue" attribute is hard coded in each field as of now. The change I can think of is

<field vendorName="Vendor 1" tagValue="736d79d9-1689-419e-c27a-1867f613fd80" contentControlTagREFS="PlaceholderNonRecursiveA"/>
<contentControl type="2" tag="PlaceholderNonRecursiveC" attrToPick="vendorName"/>

to

<field vendorName="Vendor 1" id="736d79d9-1689-419e-c27a-1867f613fd80" contentControlTagREFS="PlaceholderNonRecursiveA"/>
<contentControl type="2" tag="PlaceholderNonRecursiveC" attrToPick="vendorName" tagAttrToPick="id"/>

I think this way the hard coding of tagValue will be avoided. As it is an optional attribute if I don't choose to use it I'll
update xml to

<field vendorName="Vendor 1" id="736d79d9-1689-419e-c27a-1867f613fd80" contentControlTagREFS="PlaceholderNonRecursiveA"/>
<contentControl type="2" tag="PlaceholderNonRecursiveC" attrToPick="vendorName"/>

4. Content control tag length can be upto 64. Because of this in the examples I ensure that (tag + Guid) <= 64. Also each content control tag inside a template should be unique. n each of the samples I'm generating Word document from some data. I'll explain why setting tag is useful with the use case

WordTemplate + Data from database -> Generate Document -> Send document to user -> User changes a field(e.g. Vendor Name having Tag VendorName) in the document -> Requirement is to update the database for this change(e.g. user can upload document through some service/site and then changes made by user needs to be applied to database)

For such cases setting tag is useful. Else there is no need to set the Tag.

As per the use case we need to parse the uploaded document and identify the changes. As per convention each vendor name will be "VendorName:Guid". From here I can get the Guid corresponding to this Vendor. Now I have the Guid as well as the updated values. From this I can easily update the Vendor info present in database.

I chose a very simple example just to show the purpose behing setting Tag to "Tag:Guid" in generated document.

5. Container controls are optional. They are required only if I require the document to be refreshed(as per my samples). The first time I generate a document I write the container control to CustomXmlPart. Next time onwards I swap the container control with the control(Xml) written in the CustomXmlPart. Basically the container control ensures the validity of document while doing Refresh. Even though I have container control is templates I don't use it in "SampleDocumentGenerator. I have removed the container control and the template is https://skydrive.live.com/embed?cid=8AA5F5C7517D4722&resid=8AA5F5C7517D4722%21219&authkey=ALt2O0-6wYodYO0. You can try this template with "SampleDocumentGenerator".

Thanks & Regards,

Atul

Coordinator
Jan 31, 2012 at 7:17 PM

Jorge, I've updated the source code and added a new sample showing this approach. It's not optimized(mainly the method where Xml is parsed as I'm repeatedly iterating on Xml for each content control). However from sample point of view it's quite good. I may work to improve it later. Thanks.

Regards,

Atul

Feb 1, 2012 at 3:36 AM
Edited Feb 1, 2012 at 12:15 PM

My colleagues and I really appreciate what you have done, Atul. It will be very useful in our project! Thank you very much!

Some considerations:

1) You said: "each content control tag inside a template should be unique". Is it mandatory? If I show a vendor name in several parts of my document, do I really need to define tags with different names?

My intention is simplify the automatic generation of generic xml. If I could use the tag name in xml with the same content control name in templates and to repeat a name of a content control associate to a same value from a xml, the generic xml could be reduced.

2) If we write a generic code to generate automatically the generic xml from a class, all the process will be generic. e.g. generate automatically the generic xml from Order class in TestData.cs file or from another class representing the values from my Word document.


3) I will open a new discussion about "update the database from changes in word document" for facilitate search from other people.

Regards,

Jorge