-2

I am using StringReader to read an xml stream and then I want to read that stream as XML to Serialize and Desiarilize it. Although it was working fine for some reason my XmlReader variable returns null. The XML as I see is the same as before.

My code is:

  StringReader stringReader = new StringReader(result);
        stringReader.ReadLine();//omit the first line that contains xml encoding
        XmlReader xmlReader = XmlReader.Create(stringReader);
        XmlSerializer XmlSerializer = new XmlSerializer(typeof(ResponseDoc));
        ResponseDoc XmlResponseDoc = (ResponseDoc)XmlSerializer.Deserialize(xmlReader);

The XML format of the result viariable is

    <?xml version="1.0" encoding="utf-8"?>
<ResponseDoc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <response>
    <entitylineNumber>1</entitylineNumber>
    <statusCode>Success</statusCode>
    <entityUid>0BA0C06E9CBAA3D890D025A37A577DDB074BA9A9</entityUid>
    <entityMark>1000000115468</entityMark>
  </response>
</ResponseDoc>

My xmlReader variable is null .I also tried to remove the stringReader.ReadLine(); but it didn't change anything. I used that because if the encoding line exists it fails to serialize it.

The ResponseDoc class :

#region XSD Schema
        [XmlRoot(ElementName = "Responses", Namespace = "")]
        public partial class ResponseDoc
        {

            private ResponseType[] responseField;

            /// <remarks/>
            [System.Xml.Serialization.XmlElement(ElementName = "Response", Namespace = "")]
            public ResponseType[] response
            {
                get
                {
                    return this.responseField;
                }
                set
                {
                    this.responseField = value;
                }
            }
        }

        [XmlRoot(ElementName = "Response", Namespace = "")]
        public partial class ResponseType
        {

            private int entitylineNumberField;

            private string statusCodeField;

            private string uid;

            private string markUid;

            private ResponseTypeErrors[] responseTypeErrors;

            /// <remarks/>
            [XmlElement(ElementName = "inv_number", Namespace = "")]
            public string entitylineNumber
            {
                get
                {
                    return this.entitylineNumberField.ToString();
                }
                set
                {
                    this.entitylineNumberField = int.Parse(value);
                }
            }

            /// <remarks/>
            [XmlElement(ElementName = "StatusCode", Namespace = "")]
            public string statusCode
            {
                get
                {
                    return this.statusCodeField;
                }
                set
                {
                    this.statusCodeField = value;
                }
            }

            [XmlElement(ElementName = "Uid", Namespace = "")]
            public string Uid
            {
                get
                {
                    return this.uid;
                }
                set
                {
                    this.uid = value;
                }
            }

            [XmlElement(ElementName = "Mark", Namespace = "")]
            public string Mark
            {
                get
                {
                    return this.markUid;
                }
                set
                {
                    this.markUid = value;
                }
            }
            [XmlElement(ElementName = "Errors", Namespace = "")]
            public ResponseTypeErrors[] Errors
            {
                get
                {
                    return this.responseTypeErrors;
                }
                set
                {
                    this.responseTypeErrors = value;
                }
            }
        }

        [XmlRoot(ElementName = "Errors", Namespace = "")]
        public partial class ResponseTypeErrors
        {

            private ErrorType[] errorField;

            /// <remarks/>
            [System.Xml.Serialization.XmlElementAttribute("Error")]
            public ErrorType[] error
            {
                get
                {
                    return this.errorField;
                }
                set
                {
                    this.errorField = value;
                }
            }
        }

        [XmlRoot(ElementName = "Error", Namespace = "")]
        public partial class ErrorType
        {

            private string messageField;

            private int codeField;

            /// <remarks/>
            [XmlElement(ElementName = "Message", Namespace = "")]
            public string message
            {
                get
                {
                    return this.messageField;
                }
                set
                {
                    this.messageField = value;
                }
            }

            /// <remarks/>
            [XmlElement(ElementName = "Code", Namespace = "")]
            public int code
            {
                get
                {
                    return this.codeField;
                }
                set
                {
                    this.codeField = value;
                }
            }

        }

        #endregion

UPDATE: I see that all of you give me answer about my XSD schema. The problem is not to deserialize the XML. My problem is that the variable xmlReader in

XmlReader xmlReader = XmlReader.Create(stringReader);

is {None} altough I have the xml answer in the result variable and I pass it to stringReader. I don't understand why I some give me negative in my question. I believe it is quite clear what is my problem and I am sure I give enough information about my code.If I knew more I would solve it myself. I have upload a screenshot as well with the strinReader and the xmlReader Screenshot

rippergr
  • 182
  • 2
  • 20
  • Did you try to validata that XML against some online XML Validators? It doesn't seems correct – Steve Sep 14 '19 at 09:34
  • @Steve I cannot change the XML . It comes from an external service as an answer – rippergr Sep 14 '19 at 09:37
  • It is invalid if it is stuffed in a single XML file. Are you sure that you are not concatenatig two or more XML together? Notice how do you have two __ this is not allowed in XML specs. You could have only one and only at the beginning of the file – Steve Sep 14 '19 at 09:38
  • Your XML doesn't have a root, a workaround would be wrapping the service response in a root element. e.g. `` – Rashid Ali Sep 14 '19 at 09:44
  • @Steve Sorry , you are right . I paste the same XML twice. I edit my question – rippergr Sep 14 '19 at 09:47
  • @RashidAli XML root is ResponseDoc – rippergr Sep 14 '19 at 09:48
  • @rippergr Which variable/value exactly is `null`? The method `XmlReader.Create()` should return an `XmlReader` object when the method doesn't fail with an exception. Please [edit] your question to include a [mcve], which can be compiled and tested by others. – Progman Sep 14 '19 at 09:51
  • @rippergr I was able to run this code to deserialize the XML, can you post your `ResponseDoc` class structure? – Rashid Ali Sep 14 '19 at 09:57
  • @Progman in XmlReader xmlReader = XmlReader.Create(stringReader); the xmlReader is null. It doesn't fail in any point. It is just null. My program crashes at ResponseDoc XmlResponseDoc = (ResponseDoc)XmlSerializer.Deserialize(xmlReader); – rippergr Sep 14 '19 at 10:01
  • @RashidAli In this page https://stackoverflow.com/questions/57748327/deserialize-an-xml-string-based-on-class-file-generated-with-xsd you can see the ResponseDoc class. It's in the answer – rippergr Sep 14 '19 at 10:03
  • Please show us how you have defined the class ResponseDoc. It doesn't match your XML file – Steve Sep 14 '19 at 10:06
  • There is no `` in your XML. – H H Sep 14 '19 at 10:14
  • And no `inv_number` and I'm not so sure that `StatusCode == statusCode` etc. – H H Sep 14 '19 at 10:16

2 Answers2

1

ElementName in System.Xml.Serialization.XmlElement attribute of each property must match the element name in XML. Do the following and you are done with the fix:

ElementName for ResponseDoc class should be ResponseDoc instead of Responses

and in ResponseDoc class ElementName for response property should be response instead of Response:

ResponseDoc class should look like:

[XmlRoot(ElementName = "ResponseDoc", Namespace = "")]
public partial class ResponseDoc
{

    private ResponseType[] responseField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElement(ElementName = "response", Namespace = "")]
    public ResponseType[] response
    {
        get
        {
            return this.responseField;
        }
        set
        {
            this.responseField = value;
        }
    }
}

and in ResponseType class make sure ElementName for each property is according to XML:

ElementName for entitylineNumber property should be entitylineNumber instead of inv_number

ElementName for statusCode property should be statusCode instead of StatusCode

same rule for other properties as below:

public partial class ResponseType
    {
        private int entitylineNumberField;

        private string statusCodeField;

        private string uid;

        private long entityMark;

        [XmlElement(ElementName = "entitylineNumber", Namespace = "")]
        public string entitylineNumber
        {
            get
            {
                return this.entitylineNumberField.ToString();
            }
            set
            {
                this.entitylineNumberField = int.Parse(value);
            }
        }

        /// <remarks/>
        [XmlElement(ElementName = "statusCode", Namespace = "")]
        public string statusCode
        {
            get
            {
                return this.statusCodeField;
            }
            set
            {
                this.statusCodeField = value;
            }
        }

        [XmlElement(ElementName = "entityUid", Namespace = "")]
        public string Uid
        {
            get
            {
                return this.uid;
            }
            set
            {
                this.uid = value;
            }
        }


        [XmlElement(ElementName = "entityMark", Namespace = "")]
        public string EntityMark
        {
            get
            {
                return this.entityMark.ToString();
            }
            set
            {
                this.entityMark = long.Parse(value);
            }
        }
    }
Rashid Ali
  • 587
  • 3
  • 13
  • My problem is that in XmlReader xmlReader = XmlReader.Create(stringReader); The xmlReader is null. It's not in the xml reading after that. If I get the data in xmlReader variable everything works fine – rippergr Sep 15 '19 at 16:47
  • Thanks,your code worked. I still don't understand what is the problem. My xmlReader is still None but it returns the other variables. How can this hapens? – rippergr Sep 16 '19 at 06:29
1

If you are ok with using a Nuget package, I created a parser for you using my open source package XmlMirror.

Install-Package XmlMirror.Runtime -Version 2.2.1

I posted a video on how I made the parser here: https://youtu.be/iGRWDlzVq6s

I added this project as a sample project (hope you don't mind me using your Xml response class). So if you clone Xml Mirror, your sample project is installed with it:

The full code for Xml Mirror is available here: https://github.com/DataJuggler/XmlMirror

The code for your sample is here:

https://github.com/DataJuggler/XmlMirror/tree/master/XmlMirrror/Samples/ResponseLibrary

Here is the code I created to parse your object (I woke up before my dog and wrote this real fast to show you how easy XmlMirror is). It took longer to write this post than build this project.

Xml Mirror uses reflection, so you have to create a class library.

I created this class called Response.cs:

namespace ResponseLibrary
{

    #region class Response
    /// <summary>
    /// This class is a response from an API call
    /// </summary>
    public class Response
    {

        #region Private Variables
        private int entitylineNumber;
        private string statusCode;
        private string entityUID;
        private string entityMark;
        #endregion

        #region Properties

            #region EntitylineNumber
            /// <summary>
            /// This property gets or sets the value for 'EntitylineNumber'.
            /// </summary>
            public int EntitylineNumber
            {
                get { return entitylineNumber; }
                set { entitylineNumber = value; }
            }
            #endregion

            #region EntityMark
            /// <summary>
            /// This property gets or sets the value for 'EntityMark'.
            /// </summary>
            public string EntityMark
            {
                get { return entityMark; }
                set { entityMark = value; }
            }
            #endregion

            #region EntityUID
            /// <summary>
            /// This property gets or sets the value for 'EntityUID'.
            /// </summary>
            public string EntityUID
            {
                get { return entityUID; }
                set { entityUID = value; }
            }
            #endregion

            #region StatusCode
            /// <summary>
            /// This property gets or sets the value for 'StatusCode'.
            /// </summary>
            public string StatusCode
            {
                get { return statusCode; }
                set { statusCode = value; }
            }
            #endregion

        #endregion

    }
    #endregion

   }

Next install nuget package XmlMirror.Runtime

Here is the parser class created using XmlMirror:

The parser uses partial classes, so it is one class, but two files:

ResponsesParser.base.cs

#region using statements

using ResponseLibrary;
using DataJuggler.Core.UltimateHelper;
using System;
using System.Collections.Generic;
using XmlMirror.Runtime.Objects;
using XmlMirror.Runtime.Util;

#endregion

namespace ResponseParserTest.Parsers
{

    #region class ResponsesParser : ParserBaseClass
    /// <summary>
    /// This class is used to parse 'Response' objects.
    /// </summary>
    public partial class ResponsesParser : ParserBaseClass
    {

        #region Methods

            #region ParseResponse(string responseXmlText)
            /// <summary>
            /// This method is used to parse an object of type 'Response'.
            /// </summary>
            /// <param name="responseXmlText">The source xml to be parsed.</param>
            /// <returns>An object of type 'Response'.</returns>
            public Response ParseResponse(string responseXmlText)
            {
                // initial value
                Response response = null;

                // if the sourceXmlText exists
                if (TextHelper.Exists(responseXmlText))
                {
                    // create an instance of the parser
                    XmlParser parser = new XmlParser();

                    // Create the XmlDoc
                    this.XmlDoc = parser.ParseXmlDocument(responseXmlText);

                    // If the XmlDoc exists and has a root node.
                    if ((this.HasXmlDoc) && (this.XmlDoc.HasRootNode))
                    {
                        // Create a new response
                        response = new Response();

                        // Perform preparsing operations
                        bool cancel = Parsing(this.XmlDoc.RootNode, ref response);

                        // if the parsing should not be cancelled
                        if (!cancel)
                        {
                            // Parse the 'Response' object
                            response = ParseResponse(ref response, this.XmlDoc.RootNode);

                            // Perform post parsing operations
                            cancel = Parsed(this.XmlDoc.RootNode, ref response);

                            // if the parsing should be cancelled
                            if (cancel)
                            {
                                // Set the 'response' object to null
                                response = null;
                            }
                        }
                    }
                }

                // return value
                return response;
            }
            #endregion

            #region ParseResponse(ref Response response, XmlNode xmlNode)
            /// <summary>
            /// This method is used to parse Response objects.
            /// </summary>
            public Response ParseResponse(ref Response response, XmlNode xmlNode)
            {
                // if the response object exists and the xmlNode exists
                if ((response != null) && (xmlNode != null))
                {
                    // get the full name of this node
                    string fullName = xmlNode.GetFullName();

                    // Check the name of this node to see if it is mapped to a property
                    switch(fullName)
                    {
                        case "ResponseDoc.response.entitylineNumber":

                            // Set the value for response.EntitylineNumber
                            response.EntitylineNumber = NumericHelper.ParseInteger(xmlNode.FormattedNodeValue, 0, -1);

                            // required
                            break;

                        case "ResponseDoc.response.entityMark":

                            // Set the value for response.EntityMark
                            response.EntityMark = xmlNode.FormattedNodeValue;

                            // required
                            break;

                        case "ResponseDoc.response.entityUid":

                            // Set the value for response.EntityUID
                            response.EntityUID = xmlNode.FormattedNodeValue;

                            // required
                            break;

                        case "ResponseDoc.response.statusCode":

                            // Set the value for response.StatusCode
                            response.StatusCode = xmlNode.FormattedNodeValue;

                            // required
                            break;

                    }

                    // if there are ChildNodes
                    if (xmlNode.HasChildNodes)
                    {
                        // iterate the child nodes
                         foreach(XmlNode childNode in xmlNode.ChildNodes)
                        {
                            // append to this Response
                            response = ParseResponse(ref response, childNode);
                        }
                    }
                }

                // return value
                return response;
            }
            #endregion

        #endregion

    }
    #endregion

}

ResponsesParser.custom.cs

#region using statements

using ResponseLibrary;
using XmlMirror.Runtime.Objects;

#endregion

namespace ResponseParserTest.Parsers
{

    #region class ResponsesParser : ParserBaseClass
    /// <summary>
    /// This class is used to parse 'Response' objects.
    /// </summary>
    public partial class ResponsesParser : ParserBaseClass
    {

        #region Events

            #region Parsing(XmlNode xmlNode)
            /// <summary>
            /// This event is fired BEFORE the collection is initialized.
            /// </summary>
            /// <param name="xmlNode"></param>
            /// <returns>True if cancelled else false if not.</returns>
            public bool Parsing(XmlNode xmlNode)
            {
                // initial value
                bool cancel = false;

                // Add any pre processing code here. Set cancel to true to abort parsing this collection.

                // return value
                return cancel;
            }
            #endregion

            #region Parsing(XmlNode xmlNode, ref Response response)
            /// <summary>
            /// This event is fired when a single object is initialized.
            /// </summary>
            /// <param name="xmlNode"></param>
            /// <param name="response"></param>
            /// <returns>True if cancelled else false if not.</returns>
            public bool Parsing(XmlNode xmlNode, ref Response response)
            {
                // initial value
                bool cancel = false;

                // Add any pre processing code here. Set cancel to true to abort adding this object.

                // return value
                return cancel;
            }
            #endregion

            #region Parsed(XmlNode xmlNode, ref Response response)
            /// <summary>
            /// This event is fired AFTER the response is parsed.
            /// </summary>
            /// <param name="xmlNode"></param>
            /// <param name="response"></param>
            /// <returns>True if cancelled else false if not.</returns>
            public bool Parsed(XmlNode xmlNode, ref Response response)
            {
                // initial value
                bool cancel = false;

                // Add any post processing code here. Set cancel to true to abort adding this object.

                // return value
                return cancel;
            }
            #endregion

        #endregion

    }
    #endregion

}

Then to use the parser, all you do is something like this:

(I am using a button click since I wrote a quick Windows Form to test this):

    // xml
    string xml = File.ReadAllText(labelTextBoxBrowserControl1.Text);

    // Create a new instance of a 'ResponsesParser' object.
    ResponsesParser parser = new ResponsesParser();

    // load the response
    Response response = parser.ParseResponse(xml);

    // if the response object exists
    if (response != null)
    {   
        // set each property from the response
        EntityLineNumberControl.Text = response.EntitylineNumber.ToString();
        EntityUIDControl.Text = response.EntityUID;
        StatusCodeControl.Text = response.StatusCode;
        EntityMarkControl.Text = response.EntityMark;
    }

I used to have to parse Xml as my one of my main jobs when I wrote Xml Mirror.

It also parses collections, but I didn't build one for this example.

  • My problem is that in XmlReader xmlReader = XmlReader.Create(stringReader); The xmlReader is null. It's not in the xml reading after that. If I get the data in xmlReader variable everything works fine – rippergr Sep 15 '19 at 16:48
  • Thanks for your time. I saw your video and your code. I will sure check it more thoroughly because it seems very promising if you work with XML data. Right now @Rashid Ali code works fine – rippergr Sep 16 '19 at 06:31