XDocument or XmlDocument to JSON with C#

[Originally Posted By]: http://stackoverflow.com/questions/26605493/xdocument-or-xmldocument-to-json-with-c-sharp

I have this XML which is great:

<Products>
  <Product ProductCode="C1010" CategoryName="Coins" />
  <Product ProductCode="C1012" CategoryName="Coins" />
  <Product ProductCode="C1013" CategoryName="Coins" />
</Products>

but it outputs to this JSON:

{"Products":{"Product":[{"@ProductCode":"C1010","@CategoryName":"Coins"},
                         {"@ProductCode":"C1012","@CategoryName":"Coins"},     
                         {"@ProductCode":"C1013","@CategoryName":"Coins"}]}}

I would like no ‘Product’ sublevel in my json because all three lines are a product. This is my C# code:

//x is an XDocument. 
JsonConvert.SerializeXNode(x, Formatting.None, false)
//JsonConvert.SerializeXNode(x); //I also tried without the formatting and the boolean. 

When I ‘convert’ an XDocument to XmlDocument and use:

var xmlDocument = new System.Xml.XmlDocument();
using (var xmlReader = x.CreateReader())
{
    xmlDocument.Load(xmlReader);
}
JsonConvert.SerializeXmNode(xmlDocument);

It gives me exactly the same output. So how can I modify my JSON parsing such that I have a simple list of products. I prefer the cleanest solution.

To be perhaps a bit more clear, I’d something like this as output:

[{"@ProductCode":"C1010","@CategoryName":"Coins"},
  {"@ProductCode":"C1012","@CategoryName":"Coins"},     
  {"@ProductCode":"C1013","@CategoryName":"Coins"}]
shareedit

Use the method call

JsonConvert.SerializeXNode(x, Formatting.None, true);

this will omit the root node and should create what you expect.

shareedit

Model always null on XML POST

[Originally Posted By]: http://stackoverflow.com/questions/14068288/model-always-null-on-xml-post

I’m currently working on an integration between systems and I’ve decided to use WebApi for it, but I’m running into an issue…

Let’s say I have a model:

public class TestModel
{
    public string Output { get; set; }
}

and the POST method is:

public string Post(TestModel model)
{
    return model.Output;
}

I create a request from Fiddler with the header:

User-Agent: Fiddler
Content-Type: "application/xml"
Accept: "application/xml"
Host: localhost:8616
Content-Length: 57

and body:

<TestModel><Output>Sito</Output></TestModel>

The model parameter in the method Post is always null and I have no idea why. Does anyone have a clue?

shareedit

Two things:

  1. You don’t need quotes "" around the content type and accept header values in Fiddler:
    User-Agent: Fiddler
    Content-Type: application/xml
    Accept: application/xml
    
  2. Web API uses the DataContractSerializer by default for xml serialization. So you need to include your type’s namespace in your xml:
    <TestModel xmlns="http://schemas.datacontract.org/2004/07/YourMvcApp.YourNameSpace">
        <Output>Sito</Output>
    </TestModel>
    

    Or you can configure Web API to use XmlSerializer in your WebApiConfig.Register:

    config.Formatters.XmlFormatter.UseXmlSerializer = true;
    

    Then you don’t need the namespace in your XML data:

    <TestModel><Output>Sito</Output></TestModel>
    
shareedit

While the answer is already awarded, I found a couple other details worth considering.

The most basic example of an XML post is generated as part of a new WebAPI project automatically by visual studio, but this example uses a string as an input parameter.

Simplified Sample WebAPI controller generated by Visual Studio

using System.Web.Http;
namespace webAPI_Test.Controllers
{
    public class ValuesController : ApiController
    {
        // POST api/values
        public void Post([FromBody]string value)
        {
        }
    }
}

This is not very helpful, because it does not address the question at hand. Most POST web services have rather complex types as parameters, and likely a complex type as a response. I will augment the example above to include a complex request and complex response…

Simplified sample but with complex types added

using System.Web.Http;
namespace webAPI_Test.Controllers
{
    public class ValuesController : ApiController
    {
        // POST api/values
        public MyResponse Post([FromBody] MyRequest value)
        {
            var response = new MyResponse();
            response.Name = value.Name;
            response.Age = value.Age;
            return response;
        }
    }

    public class MyRequest
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class MyResponse
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

At this point, I can invoke with fiddler..

Fiddler Request Details

Request Headers:

User-Agent: Fiddler
Host: localhost:54842
Content-Length: 63

Request Body:

<MyRequest>
   <Age>99</Age>
   <Name>MyName</Name>
</MyRequest>

… and when placing a breakpoint in my controller I find the request object is null. This is because of several factors…

  • WebAPI defaults to using DataContractSerializer
  • The Fiddler request does not specify content type, or charset
  • The request body does not include XML declaration
  • The request body does not include namespace definitions.

Without making any changes to the web service controller, I can modify the fiddler request such that it will work. Pay close attention to the namespace definitions in the xml POST request body. Also, ensure the XML declaration is included with correct UTF settings that match the request header.

Fixed Fiddler request body to work with Complex datatypes

Request Headers:

User-Agent: Fiddler
Host: localhost:54842
Content-Length: 276
Content-Type: application/xml; charset=utf-16

Request body:

<?xml version="1.0" encoding="utf-16"?>
<MyRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/webAPI_Test.Controllers">
    <Age>99</Age>
    <Name>MyName</Name>
</MyRequest>

Notice how the namepace in the request refers to the same namespace in my C# controller class (kind of). Because we have not altered this project to use a serializer other than DataContractSerializer, and because we have not decorated our model (class MyRequest, or MyResponse) with specific namespaces, it assumes the same namespace as the WebAPI Controller itself. This is not very clear, and is very confusing. A better approach would be to define a specific namespace.

To define a specific namespace, we modify the controller model. Need to add reference to System.Runtime.Serialization to make this work.

Add Namespaces to model

using System.Runtime.Serialization;
using System.Web.Http;
namespace webAPI_Test.Controllers
{
    public class ValuesController : ApiController
    {
        // POST api/values
        public MyResponse Post([FromBody] MyRequest value)
        {
            var response = new MyResponse();
            response.Name = value.Name;
            response.Age = value.Age;
            return response;
        }
    }

    [DataContract(Namespace = "MyCustomNamespace")]
    public class MyRequest
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Age { get; set; }
    }

    [DataContract(Namespace = "MyCustomNamespace")]
    public class MyResponse
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Age { get; set; }
    }
}

Now update the Fiddler request to use this namespace…

Fiddler request with custom namespace

<?xml version="1.0" encoding="utf-16"?>
<MyRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyCustomNamespace">
    <Age>99</Age>
    <Name>MyName</Name>
</MyRequest>

We can take this idea even further. If a empty string is specified as the namespace on the model, no namespace in the fiddler request is required.

Controller with empty string namespace

using System.Runtime.Serialization;
using System.Web.Http;

namespace webAPI_Test.Controllers
{
    public class ValuesController : ApiController
    {
        // POST api/values
        public MyResponse Post([FromBody] MyRequest value)
        {
            var response = new MyResponse();
            response.Name = value.Name;
            response.Age = value.Age;
            return response;
        }
    }

    [DataContract(Namespace = "")]
    public class MyRequest
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Age { get; set; }
    }

    [DataContract(Namespace = "")]
    public class MyResponse
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Age { get; set; }
    }
}

Fiddler request with no namespace declared

<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
    <Age>99</Age>
    <Name>MyName</Name>
</MyRequest>

Other Gotchas

Beware, DataContractSerializer is expecting the elements in the XML payload to be ordered alphabetically by default. If the XML payload is out of order you may find some elements are null (or if datatype is an integer it will default to zero, or if it is a bool it defaults to false). For example, if no order is specified and the following xml is submitted…

XML body with incorrect ordering of elements

<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
    <Name>MyName</Name>
    <Age>99</Age>
</MyRequest>  

… the value for Age will default to zero. If nearly identical xml is sent …

XML body with correct ordering of elements

<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
    <Age>99</Age>
    <Name>MyName</Name>
</MyRequest> 

then the WebAPI controller will correctly serialize and populate the Age parameter. If you wish to change the default ordering so the XML can be sent in a specific order, then add the ‘Order’ element to the DataMember Attribute.

Example of specifying a property order

using System.Runtime.Serialization;
using System.Web.Http;

namespace webAPI_Test.Controllers
{
    public class ValuesController : ApiController
    {
        // POST api/values
        public MyResponse Post([FromBody] MyRequest value)
        {
            var response = new MyResponse();
            response.Name = value.Name;
            response.Age = value.Age;
            return response;
        }
    }

    [DataContract(Namespace = "")]
    public class MyRequest
    {
        [DataMember(Order = 1)]
        public string Name { get; set; }

        [DataMember(Order = 2)]
        public int Age { get; set; }
    }

    [DataContract(Namespace = "")]
    public class MyResponse
    {
        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Age { get; set; }
    }
}

In this example, the xml body must specify the Name element before the Age element to populate correctly.

Conclusion

What we see is that a malformed or incomplete POST request body (from perspective of DataContractSerializer) does not throw an error, rather is just causes a runtime problem. If using the DataContractSerializer, we need to satisfy the serializer (especially around namespaces). I have found using a testing tool a good approach – where I pass an XML string to a function which uses DataContractSerializer to deserialize the XML. It throws errors when deserialization cannot occur. Here is the code for testing an XML string using DataContractSerializer (again, remember if you implement this, you need to add a reference to System.Runtime.Serialization).

Example Testing Code for evaluation of DataContractSerializer de-serialization

public MyRequest Deserialize(string inboundXML)
{
    var ms = new MemoryStream(Encoding.Unicode.GetBytes(inboundXML));
    var serializer = new DataContractSerializer(typeof(MyRequest));
    var request = new MyRequest();
    request = (MyRequest)serializer.ReadObject(ms);

    return request;
}

Options

As pointed out by others, the DataContractSerializer is the default for WebAPI projects using XML, but there are other XML serializers. You could remove the DataContractSerializer and instead use XmlSerializer. The XmlSerializer is much more forgiving on malformed namespace stuff.

Another option is to limit requests to using JSON instead of XML. I have not performed any analysis to determine if DataContractSerializer is used during JSON deserialization, and if JSON interaction requires DataContract attributes to decorate the models.

shareedit

Read a XML (from a string) and get some fields – Problems reading XML

[Originally Posted By]: http://stackoverflow.com/questions/8401280/read-a-xml-from-a-string-and-get-some-fields-problems-reading-xml

You should use LoadXml method, not Load:

xmlDoc.LoadXml(myXML); 

Load method is trying to load xml from a file and LoadXml from a string. You could also use XPath:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);

string xpath = "myDataz/listS/sog";
var nodes = xmlDoc.SelectNodes(xpath);

foreach (XmlNode childrenNode in nodes)
{
    HttpContext.Current.Response.Write(childrenNode.SelectSingleNode("//field1").Value);
}
shareedit

Examples of using XQuery to update XML Data in SQL Server

https://www.mssqltips.com/sqlservertip/2738/examples-of-using-xquery-to-update-xml-data-in-sql-server/

Problem

What is the most efficient way to modify XML values? To allow for precise modification of XML nodes and values, theXQuery XML query language employs an extension known as the XML Data Modification Language (DML). The XML DML uses the XML modify() method, which makes use of three operational keyword sets: insert, replace value of, anddelete. Let’s take a closer look.

Solution

Inserting Nodes

The insert keyword uses the following structure:

insert Expression1 ( {as first | as last} into | after | before Expression2 )

The insert operation can be used to insert nodes into, after, or before existing nodes. In order to demonstrate some examples, let’s create a table with a record containing sample untyped XML:

CREATE TABLE HR_XML (ID INT IDENTITY, Salaries XML)
GO
INSERT HR_XML VALUES(
    '<Salaries>
      <Marketing>
        <Employee ID="1" tier="4">
          <Salary>42000</Salary>
        </Employee>
        <Employee ID="2" tier="1">
          <Salary>52000</Salary>
        </Employee>
        <Employee ID="3" tier="4">
          <Salary>48000</Salary>
        </Employee>
      </Marketing>
    </Salaries>
    '
)
GO

If we would like to create a new department node named ‘Accounting’, we can do so using modify() and insert:

UPDATE HR_XML
SET Salaries.modify('insert <Accounting /> into (/Salaries)[1]')
GO

 

The Accounting node was added inside the Salaries node by simply using the insert...into format.

The Accounting node was added inside the Salaries node by simply using the insert…into format.

Singleton Designation

You may notice that we designated a singleton (single node value) representation of ‘[1]’ for Expression 2 (the Salaries destination node). This is because the insert keyword requires that Expression 2 be a single node. Even though there is only one Salaries node, the singleton is still required. Attempting to run the query without the singleton will result in an error:

UPDATE HR_XML
SET Salaries.modify('insert <Accounting /> into (/Salaries)')
GO

 

Singleton Designation

Insert Node as First

We see that the node was placed after the only other existing node residing on the same level (the Marketing node) by default. Instead, we want the Accounting node to be the first node under the Salaries node. To make this change, we will use the format insert…as first:

UPDATE HR_XML
SET Salaries.modify('insert <Accounting /> as first into (/Salaries)[1]')
GO

 

Insert Node as First

We now have one empty Accounting node at the top, and one at the bottom. We’ll remove the bottom one later.

We are told that we have a new employee joining the Accounting department. Let’s populate the Accounting node with a new Employee node, which should include node attributes (ID, tier):

UPDATE HR_XML
SET Salaries.modify('insert <Employee ID="4" tier="4" /> into (/Salaries/Accounting)[1]')
GO

 

Let's populate the Accounting node with a new Employee node, which should include node attributes (ID, tier):

Going back for a moment to the subject of singletons; if we had used ‘[2]’ as the singleton designation, the new Employee node would have been placed in the bottom Accounting node.

Insert by Identifying a Specific Node

What if we needed to insert a new node into a specific Employee node in the Marketing department? We can identify the correct employee by their ID attribute, using the ‘@ID’ argument:

UPDATE HR_XML
SET Salaries.modify('insert <Projects /> into (/Salaries/Marketing/Employee[@ID=("2")])[1]')
GO

 

Insert by Identifying a Specific Node

The Projects node has been inserted into the Employee node having ID #2.

Insert Nodes with Values

We can also insert any collection of nested nodes and values. If we want to designate a new project for employee ID #2, we can do the following:

UPDATE HR_XML
SET Salaries.modify('insert <Project ID="1"><Description>Organize new 
    strategies</Description></Project> into 
    (/Salaries/Marketing/Employee[@ID=("2")]/Projects)[1]')
GO

 

Insert Nodes with Values

Insert Nodes with Values from Other Nodes

The newest employee now needs salary data. Employee #4 is at tier 4, like employee #1, and should be at the same salary range as employee #1. Let’s insert a Salary node for employee #4, and populate it with the salary amount from employee #1’s Salary node:

UPDATE HR_XML
SET Salaries.modify('insert 
<Salary>{(/Salaries/Marketing/Employee[@ID=("1")]/Salary/text())}</Salary> 
into (/Salaries/Accounting/Employee[@ID=("4")])[1]')
GO

 

Insert Nodes with Values from Other Nodes

Notice that we’ve pulled the salary amount from employee #1 by using curly braces ({}) around the path, and then wrapped it all with explicitly depicted Salary node tags.

Deleting Nodes

The delete keyword is much simpler than insert. Its syntax structure looks like:

    delete Expression

Let’s remove the redundant bottommost Accounting node from the Salaries node:

UPDATE HR_XML
SET Salaries.modify('delete (/Salaries/Accounting)[2]')
GO

We’ve deleted the empty node by indicating its singleton value (2).

Deleting Values

Node values can be deleted by employing the text() function to specify that a value, not a node, is to be removed.

The project description for project #1 has become outdated. We need to remove the description for the time being. Only employee #2 is working on this project, so we can run the following to remove the value:

UPDATE HR_XML
SET Salaries.modify('delete 
(/Salaries/Marketing/Employee[@ID=("2")]/Projects/Project[@ID="1"]/Description/text())[1]')
GO

 

Deleting Values

The Description node’s value has been successfully removed.

Replacing Values

We can use replace value of to change node values. The syntax format for replace value of is as follows:

replace value of Expression1 with Expression2

We realize that the salary amount for employee #2 is incorrect. We need to change it from 52000 to 60000. To update it to the new value, we’ll run the following:

UPDATE HR_XML
SET Salaries.modify('replace value of 
(/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] with ("60000")')
GO

 

Replacing Values

The salary amount has been updated.

Replace Values Using XQuery Arithmetic

We now find out that ALL employees in the Marketing department are to receive a 10% salary increase. We can use a script that iterates Employee nodes appropriately:

DECLARE @i INT = 1
WHILE @i <= 3
   BEGIN
 UPDATE HR_XML
 SET Salaries.modify('replace value of 
 (/Salaries/Marketing/Employee[@ID=(sql:variable("@i"))]/Salary/text())[1] 
 with (/Salaries/Marketing/Employee[@ID=(sql:variable("@i"))]/Salary)[1] * 1.01')
 SET @i+=1
   END
GO

 

Replace Values Using XQuery Arithmetic

We’ve used the XQuery multiplication operator to increase the existing value of every Salary node in Marketing by 10%. We incorporated the SQL variable ‘@i’ (inside of a sql:variable() extension function), which was used to iterate the Employee nodes. Notice that the salary amount for employee # 4 was not updated, since that employee is in the Accounting department.

Modify() Limitations

A couple of operations involving the modify() method will not work as may be expected:

Issue # 1

The modify() method cannot be used to select the results of the XQuery modify statement. The following will fail:

SELECT Salaries.modify('replace value of 
(/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] 
with ("60000")')
FROM HR_XML
GO

Modify() Limitations

This is because modify() operates directly on the XML value, whether it is a column or variable, and cannot be used in an ad-hoc operation that does not update the underlying XML value. An alternative to the above operation would be to select the XML value into a new XML variable, make the change, and then select the variable value:

DECLARE @x XML
SELECT @x = Salaries FROM HR_XML
SET @x.modify('replace value of (/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] with ("60000")')
SELECT @x
GO 

The above method can be used to check or test the modification script before actually applying it to the original XML value.

Issue # 2

Modify() also cannot be used more than once within the same UPDATE statement, on the same XML value. In other words, only one instance of modify() may be used at the same time on the XML value. For example, an operation that tries to update the salary amount of two different employees in the following manner will fail:

UPDATE HR_XML
SET Salaries.modify('replace value of 
(/Salaries/Marketing/Employee[@ID=("2")]/Salary/text())[1] with ("60000")'),
Salaries.modify('replace value of 
(/Salaries/Marketing/Employee[@ID=("1")]/Salary/text())[1] with ("60000")')
GO

 Modify() also cannot be used more than once within the same UPDATE statement

Instead, multiple updates to the same XML value must be accomplished using entirely separate UPDATE statements.

Replace Value of one Node with Value of Another

Employee #1 has reached tier 1 status. We need to update the salary amount to match that of employee #2 (60600):

UPDATE HR_XML
SET Salaries.modify('replace value of 
(/Salaries/Marketing/Employee[@ID=("1")]/Salary/text())[1] 
with (/Salaries/Marketing/Employee[@ID=("2")]/Salary)')
GO

Replace Value of a Node Attribute

We also need to change the tier number for employee #1. To specify a node attribute in the XQuery path, use the ‘@’ symbol:

UPDATE HR_XML
SET Salaries.modify('replace value of 
(/Salaries/Marketing/Employee[@ID=("1")]/@tier)[1] with "1"')
GO

We’ve successfully changed the salary amount and tier number for employee #1. The results after running both statements are as follows:

 

Replace Value of a Node Attribute

Notice that we had to perform two separate modify() operations in order to update the salary and tier data.

Conclusion

We’ve looked at examples of the XML DML extension, using the modify() XQuery method. We introduced all three operational keyword sets: insert, replace value of, and delete; and used them to perform some of the more common XML modification operations. We also pointed out some limitations and appropriate workarounds.

Next Steps
  • In all of our examples here, we’ve used untyped XML. If you are interested in furthering your XML DML skills, I suggest becoming familiar with performing the operations we have covered in this article on typed XML. An excellent article by Robert Sheldon will get you started.
  • Read these other tips related to XML

How can I query a value in SQL Server XML column

http://stackoverflow.com/questions/10344553/how-can-i-query-a-value-in-sql-server-xml-column

I have following XML stored in a XML column (called Roles) in a SQL Server database.

<root>
   <role>Alpha</role>
   <role>Beta</role>
   <role>Gamma</role>
</root>

I’d like to list all rows that have a specific role in them. This role passed by parameter.

shareedit
select
  Roles
from
  MyTable
where
  Roles.value('(/root/role)[1]', 'varchar(max)') like 'StringToSearchFor'

These pages will show you more about how to query XML in T-SQL:

Querying XML fields using t-sql

Flattening XML Data in SQL Server

EDIT

After playing with it a little bit more, I ended up with this amazing query that uses CROSS APPLY. This one will search every row (role) for the value you put in your like expression…

Given this table structure:

create table MyTable (Roles XML)

insert into MyTable values
('<root>
   <role>Alpha</role>
   <role>Gamma</role>
   <role>Beta</role>
</root>')

We can query it like this:

select * from 

(select 
       pref.value('(text())[1]', 'varchar(32)') as RoleName
from 
       MyTable CROSS APPLY

       Roles.nodes('/root/role') AS Roles(pref)
)  as Result

where RoleName like '%ga%'

You can check the SQL Fiddle here: http://sqlfiddle.com/#!3/ae0d5/13

shareedit

Simple deserialization of XML to C# object

http://www.janholinka.net/Blog/Article/11

1. Prepare XML string

  string xmlString = "<Products><Product><Id>1</Id><Name>My XML product</Name></Product><Product><Id>2</Id><Name>My second product</Name></Product></Products>";

2. Prepare C# object

  public class Product
  {
      public int Id { get; set; }
      public string Name { get; set; }
  }

3. Create XML serializer

First argument is type of object you want to get and in second argument you specify root attribute of your XML source.

  XmlSerializer serializer = new XmlSerializer(typeof(List<Product>), newXmlRootAttribute("Products"));

4. Create StringReader object

  StringReader stringReader = new StringReader(xmlString);

5. Finally, deserialize to your C# object

  List<Product> productList = (List<Product>)serializer.Deserialize(stringReader);

You can use our StringReader as argument or StreamWriter for external xml file too.

That’s all.