The function that converts a hierarchy table to XML gives us a JSON to XML converter. It is surprisingly similar to the previous function
IF OBJECT_ID (N'dbo.ToXML') IS NOT NULL DROP FUNCTION dbo.ToXML GO CREATE FUNCTION ToXML ( /*this function converts a JSONhierarchy table into an XML document. This uses the same technique as the toJSON function, and uses the 'entities' form of XML syntax to give a compact rendering of the structure */ @Hierarchy JSONHierarchy READONLY ) RETURNS NVARCHAR(MAX)--use unicode. AS BEGIN DECLARE @XMLAsString NVARCHAR(MAX), @NewXML NVARCHAR(MAX), @Entities NVARCHAR(MAX), @Objects NVARCHAR(MAX), @Name NVARCHAR(200), @Where INT, @ANumber INT, @notNumber INT, @indent INT, @CrLf CHAR(2)--just a simple utility to save typing! --firstly get the root token into place --firstly get the root token into place SELECT @CrLf=CHAR(13)+CHAR(10),--just CHAR(10) in UNIX @XMLasString ='<?xml version="1.0" ?> @Object'+CONVERT(VARCHAR(5),OBJECT_ID)+' ' FROM @hierarchy WHERE parent_id IS NULL AND valueType IN ('object','array') --get the root element /* now we simply iterate from the root token growing each branch and leaf in each iteration. This won't be enormously quick, but it is simple to do. All values, or name/value pairs within a structure can be created in one SQL Statement*/ WHILE 1=1 begin SELECT @where= PATINDEX('%[^a-zA-Z0-9]@Object%',@XMLAsString)--find NEXT token if @where=0 BREAK /* this is slightly painful. we get the indent of the object we've found by looking backwards up the string */ SET @indent=CHARINDEX(char(10)+char(13),Reverse(LEFT(@XMLasString,@where))+char(10)+char(13))-1 SET @NotNumber= PATINDEX('%[^0-9]%', RIGHT(@XMLasString,LEN(@XMLAsString+'|')-@Where-8)+' ')--find NEXT token SET @Entities=NULL --this contains the structure in its XML form SELECT @Entities=COALESCE(@Entities+' ',' ')+NAME+'="' +REPLACE(REPLACE(REPLACE(StringValue, '<', '<'), '&', '&'),'>', '>') + '"' FROM @hierarchy WHERE parent_id= SUBSTRING(@XMLasString,@where+8, @Notnumber-1) AND ValueType NOT IN ('array', 'object') SELECT @Entities=COALESCE(@entities,''),@Objects='',@name=CASE WHEN Name='-' THEN 'root' ELSE NAME end FROM @hierarchy WHERE [Object_id]= SUBSTRING(@XMLasString,@where+8, @Notnumber-1) SELECT @Objects=@Objects+@CrLf+SPACE(@indent+2) +'@Object'+CONVERT(VARCHAR(5),OBJECT_ID) --+@CrLf+SPACE(@indent+2)+'' FROM @hierarchy WHERE parent_id= SUBSTRING(@XMLasString,@where+8, @Notnumber-1) AND ValueType IN ('array', 'object') IF @Objects='' --if it is a lef, we can do a more compact rendering SELECT @NewXML='<'+COALESCE(@name,'item')+@entities+' />' ELSE SELECT @NewXML='<'+COALESCE(@name,'item')+@entities+'>' +@Objects+@CrLf++SPACE(@indent)+'</'+COALESCE(@name,'item')+'>' /* basically, we just lookup the structure based on the ID that is appended to the @Object token. Simple eh? */ --now we replace the token with the structure, maybe with more tokens in it. Select @XMLasString=STUFF (@XMLasString, @where+1, 8+@NotNumber-1, @NewXML) end return @XMLasString end
This provides you the means of converting a JSON string into XML
DECLARE @MyHierarchy JSONHierarchy,@xml XML INSERT INTO @myHierarchy select * from parseJSON('{"menu": { "id": "file", "value": "File", "popup": { "menuitem": [ {"value": "New", "onclick": "CreateNewDoc()"}, {"value": "Open", "onclick": "OpenDoc()"}, {"value": "Close", "onclick": "CloseDoc()"} ] } }}') SELECT dbo.ToXML(@MyHierarchy) SELECT @XML=dbo.ToXML(@MyHierarchy) SELECT @XML
This gives the result...
<?xml version="1.0" ?> <root> <menu id="file" value="File"> <popup> <menuitem> <item value="New" onclick="CreateNewDoc()" /> <item value="Open" onclick="OpenDoc()" /> <item value="Close" onclick="CloseDoc()" /> </menuitem> </popup> </menu> </root> (1 row(s) affected) <root><menu id="file" value="File"><popup><menuitem><item value="New" onclick="CreateNewDoc()" /><item value="Open" onclick="OpenDoc()" /><item value="Close" onclick="CloseDoc()" /></menuitem></popup></menu></root> (1 row(s) affected)
The so-called 'impedence-mismatch' between applications and databases is, I reckon, an illusion. The object-oriented nested data-structures that we receive from applications are, if the developer has understood the data correctly, merely a perspective from a particular entity of the relationships it is involved with. Whereas it is easy to shred XML documents to get the data from it to update the database, it has been trickier with other formats such as JSON. By using techniques like this, it should be possible to liberate the application, or website, programmer from having to do the mapping from the object model to the relational, and spraying the database with ad-hoc TSQL that uses the base tables or updateable views. If the database can be provided with the JSON, or the Table-Valued parameter, then there is a better chance of maintaining full transactional integrity for the more complex updates.
The database developer already has the tools to do the work with XML, but why not the simpler, and more practical JSON? I hope these two routines get you started with experimenting with this.
http://www.simple-talk.com/sql/t-sql-programming/consuming-json-strings-in-sql-server/