Asked  7 Months ago    Answers:  5   Viewed   81 times

Once a programmer decides to implement IXmlSerializable, what are the rules and best practices for implementing it? I've heard that GetSchema() should return null and ReadXml should move to the next element before returning. Is this true? And what about WriteXml - should it write a root element for the object or is it assumed that the root is already written? How should child objects be treated and written?

Here's a sample of what I have now. I'll update it as I get good responses.

public class MyCalendar : IXmlSerializable
{
    private string _name;
    private bool _enabled;
    private Color _color;
    private List<MyEvent> _events = new List<MyEvent>();


    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyCalendar")
        {
            _name    = reader["Name"];
            _enabled = Boolean.Parse(reader["Enabled"]);
            _color   = Color.FromArgb(Int32.Parse(reader["Color"]));

            if (reader.ReadToDescendant("MyEvent"))
            {
                while (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
                {
                    MyEvent evt = new MyEvent();
                    evt.ReadXml(reader);
                    _events.Add(evt);
                }
            }
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Name",    _name);
        writer.WriteAttributeString("Enabled", _enabled.ToString());
        writer.WriteAttributeString("Color",   _color.ToArgb().ToString());

        foreach (MyEvent evt in _events)
        {
            writer.WriteStartElement("MyEvent");
            evt.WriteXml(writer);
            writer.WriteEndElement();
        }
    }
}

public class MyEvent : IXmlSerializable
{
    private string _title;
    private DateTime _start;
    private DateTime _stop;


    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyEvent")
        {
            _title = reader["Title"];
            _start = DateTime.FromBinary(Int64.Parse(reader["Start"]));
            _stop  = DateTime.FromBinary(Int64.Parse(reader["Stop"]));
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("Title", _title);
        writer.WriteAttributeString("Start", _start.ToBinary().ToString());
        writer.WriteAttributeString("Stop",  _stop.ToBinary().ToString());
    }
}

Corresponding Sample XML

<MyCalendar Name="Master Plan" Enabled="True" Color="-14069085">
    <MyEvent Title="Write Code" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="???" Start="-8589241828854775808" Stop="-8589241756854775808" />
    <MyEvent Title="Profit!" Start="-8589247048854775808" Stop="-8589246976854775808" />
</MyCalendar>

 Answers

25

Yes, GetSchema() should return null.

IXmlSerializable.GetSchema Method This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return a null reference (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the XmlSchemaProviderAttribute to the class.

For both read and write, the object element has already been written, so you don't need to add an outer element in write. For example, you can just start reading/writing attributes in the two.

For write:

The WriteXml implementation you provide should write out the XML representation of the object. The framework writes a wrapper element and positions the XML writer after its start. Your implementation may write its contents, including child elements. The framework then closes the wrapper element.

And for read:

The ReadXml method must reconstitute your object using the information that was written by the WriteXml method.

When this method is called, the reader is positioned at the start of the element that wraps the information for your type. That is, just before the start tag that indicates the beginning of a serialized object. When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.

I'll agree that is a little unclear, but it boils down to "it is your job to Read() the end-element tag of the wrapper".

Tuesday, June 1, 2021
 
saad
answered 7 Months ago
30

I did some research may be following answer helps

For Attributes to have namespace prefix you have to specify a different namespace tag other than what you have specified http://foo. Following code hopefully will solve your issue. In the code i have remove the namespace for elements and added only for the attribute.

public class IncidentEvent
{
    public string EventDate { get; set; }
    public string EventTime { get; set; }

    [XmlAttribute("EventTypeText", Namespace = "http://foo")]
    public string EventTypeText { get; set; }

}

class Program
{
    static void Main(string[] args)
    {
        IncidentEvent xmlObj = new IncidentEvent()
        {
            EventDate = "2012.12.01",
            EventTime = "1:00:00",
            EventTypeText = "Beginining"
        };
        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
        ns.Add("ett", "http://foo");
        XmlSerializer serializer = new XmlSerializer(typeof(IncidentEvent));
        serializer.Serialize(Console.OpenStandardOutput(), xmlObj, ns);
        Console.WriteLine();
    }
}

http://www.w3.org/TR/2009/REC-xml-names-20091208/#defaulting

Saturday, July 31, 2021
 
juananrey
answered 4 Months ago
96

The XmlRoot attribute you added to Site is not used, because you aren't deserializing an object of type Site. You are deserializing an object of type List<Site> and that's where the serializer looks for the XmlRoot attribute.

Your workaround actually is the correct solution. However, if you are performing this deserialization often in your program, be sure to cache the XmlSerializer instance, because that specific constructor doesn't internally cache the dynamically generated assemblies - this contrasts with the constructor you normally use.

Friday, August 6, 2021
 
Rajat Gupta
answered 4 Months ago
65

1) The easiest way to tie a gesture to the UIViewControllerInteractiveTransitioning object, is making it subclass of UIPercentDrivenInteractiveTransition. Then where you implement the gesture handler, you call updateInteractiveTransition: here an example with code:

-(void)handlePinch:(UIPinchGestureRecognizer *)pinch {

    CGFloat scale = pinch.scale;
    switch (pinch.state) {
      case UIGestureRecognizerStateBegan: {
          _startScale = scale;
          self.interactive = YES;
          [self.navigationController popViewControllerAnimated:YES];
          break;
      }
      case UIGestureRecognizerStateChanged: {
          CGFloat percent = (1.0 - scale/_startScale);
          [self updateInteractiveTransition:(percent < 0.0) ? 0.0 : percent];
          break;
      }
      case UIGestureRecognizerStateEnded: {
          CGFloat percent = (1.0 - scale/_startScale);
          BOOL cancelled = ([pinch velocity] < 5.0 && percent <= 0.3);
          if (cancelled) [self cancelInteractiveTransition];
          else [self finishInteractiveTransition];
          break;
      }
      case UIGestureRecognizerStateCancelled: {
          CGFloat percent = (1.0 - scale/_startScale);
          BOOL cancelled = ([pinch velocity] < 5.0 && percent <= 0.3);
          if (cancelled) [self cancelInteractiveTransition];
          else [self finishInteractiveTransition];
          break;
      }
    }
}

This code is from https://www.captechconsulting.com/blogs/ios-7-tutorial-series-custom-navigation-transitions--more

2) The function animateTransition of UIViewControllerAnimatedTransitioning is used to perform the interactive transition. It is automatically partitioned in "keyframes" thanks to your previous call to updateInteractiveTransition. But I suppose that if you implement your startInteractiveTransition: method of UIViewControllerInteractiveTransitioning (so without using UIPercentDrivenInteractiveTransition subclass) then you are responsible to manage the fully transition (not sure about that.. sorry but the documentation in my opinion is not really clear).

Saturday, August 7, 2021
 
njai
answered 4 Months ago
40

I asked Winston Chang, the author of R6, about this on Github. According to him, the code provided in Question 1 above is the correct way of writing S3 methods for R6 classes.

Friday, October 29, 2021
 
Koerr
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share