Liquid XML Data Binder
Liquid XML Data Binder (C++, Java, VB6) / Using the Code / Loading and Saving XML / Writing XML Streams (Writing large files)
In This Topic
    Writing XML Streams (Writing large files)
    In This Topic

    When using Liquid XML Objects to build an XML document, the model is constructed in memory and then serialized. For most applications this is fine as the XML documents are of a manageable size, however if you are working on large XML files then holding the entire  document in memory may not be possible, the following demonstrates how to write a large XML document in management chunks.

    Simple Log File Example

    This example will show how to write log entries one at a time, allowing extremely large files to be created with minimal resources.

    Generated Code
    Copy Code
        #region Elements
        [LxSimpleElementDefinition("LogFile", "", ElementScopeType.GlobalElement)]
        public partial class LogFileElm : LiquidXmlObjects.SimpleLogFile.LxBase
        {
            [LxElementCt("LogEntry", "", MinOccurs = 0, MaxOccurs = LxConstants.Unbounded)]
            public List<LiquidXmlObjects.SimpleLogFile.Ns.LogEntryTypeCt> LogEntries { get; } = new List<LiquidXmlObjects.SimpleLogFile.Ns.LogEntryTypeCt>();
        }
        #endregion
    
        #region Complex Types
        [LxSimpleComplexTypeDefinition("LogEntryType", "")]
        public partial class LogEntryTypeCt : LiquidXmlObjects.SimpleLogFile.LxBase
        {
            [LxElementValue("Date", "", LxValueType.Value, XsdType.XsdDateTime, MinOccurs = 1, MaxOccurs = 1)]
            public LiquidTechnologies.XmlObjects.LxDateTime Date { get; set; }
            [LxElementValue("Severity", "", LxValueType.Enum, XsdType.Enum, MinOccurs = 1, MaxOccurs = 1, WhiteSpace = WhiteSpaceType.Preserve)]
            public LiquidXmlObjects.SimpleLogFile.Ns.LogEntryTypeCt.SeverityEnum Severity { get; set; }
            [LxElementValue("Message", "", LxValueType.Value, XsdType.XsdString, MinOccurs = 1, MaxOccurs = 1)]
            public System.String Message { get; set; } = "";
            public enum SeverityEnum
            {
                [LxEnumValue("Error")]   Error,
                [LxEnumValue("Warning")] Warning,
                [LxEnumValue("Info")]    Info,
            }
        }
        #endregion
    
    Sample Code
    Copy Code
    LxSerializer logEntrySerializer = new LxSerializer();
    
    using (XmlWriter xmlWriter = XmlWriter.Create(@"SimpleLogFile_SampleOutput.xml", new XmlWriterSettings() { Indent = true }))
    {
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("LogFile");
    
        LogEntryTypeCt log1 = new LogEntryTypeCt()
        {
            Date = LxDateTime.Now,
            Severity = LogEntryTypeCt.SeverityEnum.Warning,
            Message = "Bugs in the queue"
        };
        logEntrySerializer.SerializeSnippet(xmlWriter, log1, new XmlQualifiedName("LogEntry"), typeof(LogEntryTypeCt));
    
        LogEntryTypeCt log2 = new LogEntryTypeCt()
        {
            Date = LxDateTime.Now,
            Severity = LogEntryTypeCt.SeverityEnum.Error,
            Message = "Failed to read queue"
        };
        logEntrySerializer.SerializeSnippet(xmlWriter, log2, new XmlQualifiedName("LogEntry"), typeof(LogEntryTypeCt));
    
    
        xmlWriter.WriteEndElement();
        xmlWriter.WriteEndDocument();
    }
    

    The sample code above uses an XmlWriter to output the XML document, the code manually adds the XML structure up to the point where you need to add a large number of XML entries (typically large files have 1 parent element containing 1000s of child records). Then for each child element (LogEntry) it serializes a LogEntryTypeCt (which is the C# class generated to handle the xs:complexType LogEntryType).

    Looking more closely at the SerializeSnippet Method we can see it takes 4 parameters, first 2 are simple, the xml writer, and the object to serialize. The 3rd argument is the name of the element that will be written, this is only required if the object being serialized is based on a xs:complexType in the schema. As a xs:complexType can be used in many places it does not have an element name associated with it, so we must supply one. If the object being serialized was base on a root xs:element or an xs:element that was declared inline within the XSD Schema then we would not need to supply the element name parameter as its already been defined. The last parameter is the declared type of the object we are serializing. In this case we are writing a LogEntry and that is defined as being of type xs:complexType LogEntryType, so we need to tell the serializer the C# class that represents that type i.e. LogEntryTypeCt. The serializer uses this type to determine if the xsi:type attribute should be written out (if you omit this or get it wrong, then you will see xsi:type attributes in your XML output where you don't expect them).

    Output XML Data
    Copy Code
    <?xml version="1.0" encoding="utf-8"?>
    <LogFile>
      <LogEntry>
        <Date>2021-02-17T12:12:40.246Z</Date>
        <Severity>Warning</Severity>
        <Message>Bugs in the queue</Message>
      </LogEntry>
      <LogEntry>
        <Date>2021-02-17T12:12:40.304Z</Date>
        <Severity>Error</Severity>
        <Message>Failed to read queue</Message>
      </LogEntry>
    </LogFile>
    

    Advanced Cases

    If you need to serialize elements based on different xs:complexTypes then the output XML needs to contain a xsi:type attributes. This is automatically done when the SerializeSnippet Method is called. The following example demonstrates this.

    Sample Code
    Copy Code
    LxSerializer logEntrySerializer = new LxSerializer();
    
    using (XmlWriter xmlWriter = XmlWriter.Create(@"AdvancedLogFile_SampleOutput.xml", new XmlWriterSettings() { Indent = true }))
    {
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("LogFile");
        xmlWriter.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
    
        LogEntryTypeCt log1 = new LogEntryTypeCt()
        {
            Date = LxDateTime.Now,
            Severity = LogEntryTypeCt.SeverityEnum.Error,
            Message = "Bugs in the queue"
        };
        logEntrySerializer.SerializeSnippet(xmlWriter, log1, new XmlQualifiedName("LogEntry"), typeof(LogEntryTypeCt));
    
        EnhancedLogEntryTypeCt log2 = new EnhancedLogEntryTypeCt()
        {
            Date = LxDateTime.Now,
            Severity = LogEntryTypeCt.SeverityEnum.Error,
            Message = "Bugs in the queue",
            Suggestion = "Remove bugs in queue"
        };
        logEntrySerializer.SerializeSnippet(xmlWriter, log2, new XmlQualifiedName("LogEntry"), typeof(LogEntryTypeCt));
    
        xmlWriter.WriteEndElement();
        xmlWriter.WriteEndDocument();
    }
    
    Output XML
    Copy Code
    <?xml version="1.0" encoding="utf-8"?>
    <LogFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <LogEntry>
        <Date>2021-02-17T13:35:51.791Z</Date>
        <Severity>Error</Severity>
        <Message>Bugs in the queue</Message>
      </LogEntry>
      <LogEntry xsi:type="EnhancedLogEntryType">
        <Date>2021-02-17T13:35:51.844Z</Date>
        <Severity>Error</Severity>
        <Message>Bugs in the queue</Message>
        <Suggestion>Remove bugs in queue</Suggestion>
      </LogEntry>
    </LogFile>
    

    If you look at the output XML you will notice that the first LogEntry does not have an xsi:type attribute, this is because the type of the object being serialized (log1 which is a LogEntryTypeCt class), is the same type as the elementType passed to the SerializeSnippet Method. However if you look at the second log entry you will see it has the attribute xsi:type="EnhancedLogEntryType". This is added as the type being serialized does NOT match the type passed to SerializeSnippet Method, and so is required in order to make the XML valid.