Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xslt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用MarkLogic XSLT修改一个JSON属性?_Json_Xslt_Marklogic - Fatal编程技术网

如何使用MarkLogic XSLT修改一个JSON属性?

如何使用MarkLogic XSLT修改一个JSON属性?,json,xslt,marklogic,Json,Xslt,Marklogic,我有一些JSON、一个XPath和一个值。我想用新值替换XPath指向的JSON属性中的现有值。我想我可以用XSLT来做,但我不太擅长XSLT。这将在XQuery模块中 对于XML,我可以这样做: let $content := document { <class> <student rollno = "393"> <firstname>Dinkar</firstname> <la

我有一些JSON、一个XPath和一个值。我想用新值替换XPath指向的JSON属性中的现有值。我想我可以用XSLT来做,但我不太擅长XSLT。这将在XQuery模块中

对于XML,我可以这样做:

let $content :=
  document {
    <class> 
      <student rollno = "393"> 
        <firstname>Dinkar</firstname> 
        <lastname>Kad</lastname> 
        <nickname>Dinkar</nickname> 
        <marks>85</marks> 
      </student> 
      <student rollno = "493"> 
        <firstname>Vaneet</firstname> 
        <lastname>Gupta</lastname> 
        <nickname>Vinni</nickname> 
        <marks>95</marks> 
      </student>
    </class>
  }
let $template := 
  <xsl:stylesheet version = "1.0" 
    xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
    <xsl:template match = "node()|@*"> 
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>
    <xsl:template match="student/marks">
      <foo>bar</foo>
    </xsl:template>
  </xsl:stylesheet>
return
    xdmp:xslt-eval($template, $content)
我想以这个结尾:

{
  "SomeProperty": {
    "LowProperty1":"bar", 
    "LowProperty2":"some string", 
    "LowProperty3": [ "some string 1", "some string 2" ]
  }
}

相反,我得到的是一份原件。我尝试过一些变化,但我没有接近。我应该期待这个工作吗

除非MarkLogic做了一些我不知道的事情来扩展标准XSLT语义,否则这是行不通的。像
SomeProperty/LowProperty1这样的匹配模式不能用于寻址映射/数组树的部分。您可以在这样的树中进行匹配,但这不是很有用,因为匹配不能是上下文敏感的:给定一个映射或数组,您无法找到它在哪里,也无法找到它是如何到达的

您可能会发现阅读我关于使用XSLT 3.0转换JSON的XML Prague 2016论文很有用:

使用XSLT模板匹配转换XML的标准方法不能很好地转换为JSON,根本原因是用于表示JSON的映射/数组结构没有“节点标识”或向上导航(父指针)。在本文中的例子中,我通常发现,这种转换最简单的方法是将结构转换为XML、转换XML、然后转换回来——尽管还有其他方法可以考虑。


我一直在尝试为高阶扩展函数设计一个设计,以使这种任务更容易。我认为我还没有理想的解决方案。

问题似乎是MarkLogic的XSLT处理器处理JSON扩展的程度与它的XQuery处理器不一样
似乎被
对象-节点()
短路,其行为类似于
,复制所有子体,从而阻止执行
LowProperty1
模板(以及任何其他模板)。您可以通过向
LowProperty1
模板添加
来确认这一点,并查看消息从未被记录

据我所知,没有惯用的方法从XSLT中复制JSON节点。因此,另一种方法是在转换之前和之后简单地转换为/从
json:object
——当然,这可以在运行XSLT之前在XQuery中完成(这可能更可取)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
    xmlns:json="http://marklogic.com/xdmp/json"
    xmlns:xdmp="http://marklogic.com/xdmp">

   <xsl:template match="document-node()">
     <xsl:variable name="jsonxml" as="element()">
       <temp><xsl:sequence select="xdmp:from-json(.)"/></temp>
     </xsl:variable>
     <xsl:variable name="result" as="element(json:object)">
       <xsl:apply-templates select="$jsonxml/json:object"/>
     </xsl:variable>
     <xsl:sequence select="xdmp:to-json(json:object($result))"/>   
   </xsl:template>

    <xsl:template match="node()|@*"> 
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="json:entry[@key='LowProperty1']/json:value">
      <xsl:copy>
        <xsl:text>bar</xsl:text>
      </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

酒吧
如果设置了,则可以对JSON节点类型使用模板匹配模式:
对象-节点()
数组-节点()
数字-节点()
布尔-节点()
空-节点()
,此外还可以使用基于节点名称的XPath和匹配模式,例如
SomeProperty/LowProperty1

不幸的是,
xsl:copy
执行深度复制,这使得转换变得困难,并且没有可用于这些JSON节点的XSLT节点构造函数

因此,将JSON转换为XML、HTML和文本非常容易,但为了构建所需的转换后的JSON,您可能需要转换为@wst演示的JSON:object
,或者您可以进行一些欺骗,生成JSON文本

使用一些匹配JSON节点并生成JSON文本输出的基本模板,然后可以添加自己的专用模板来更改
SomeProperty/LowProperty1
值:

let $stuff :=
  document {
    object-node {
      "SomeProperty": object-node {
        "LowProperty1":"some string", 
        "LowProperty2":"some string", 
        "LowProperty3": array-node { "some string 1", "some string 2"}
      }
    }
  }

let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
  xdmp:xslt-eval(
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
        xdmp:dialect="1.0-ml">

      <xsl:output method="text"/>

      <xsl:variable name="lcurly" select="'&#123;'"/>
      <xsl:variable name="rcurly" select="'&#125;'"/>

      <xsl:template match="node()">
      <xsl:apply-templates select="." mode="name"/>
        <xsl:apply-templates select="." mode="value"/>
      </xsl:template>

      <xsl:template match="array-node()/node()">
        <xsl:apply-templates select="." mode="value"/>
      </xsl:template>

      <xsl:template match="node()" mode="name">
        <xsl:if test="string(node-name(.))!=''">"<xsl:value-of select="node-name(.)"/>": </xsl:if>
      </xsl:template>

      <xsl:template match="text()" mode="value">
        <xsl:text>"</xsl:text><xsl:value-of select="."/><xsl:text>"</xsl:text>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

      <xsl:template match="number-node() | boolean-node()" mode="value">
        <xsl:value-of select="."/>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

      <xsl:template match="object-node()" mode="value">
        <xsl:value-of select="$lcurly"/>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of select="$rcurly"/> 
         <xsl:if test="following-sibling::node()">,</xsl:if>
      </xsl:template>

      <xsl:template match="array-node()/object-node()" mode="value">
        <xsl:value-of select="$lcurly"/>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of select="$rcurly"/>
        <xsl:if test="following-sibling::node()">,</xsl:if>
      </xsl:template>

      <xsl:template match="array-node()" mode="value">
        <xsl:value-of select="'['"/>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of select="']'"/> 
        <xsl:if test="following-sibling::node()">,</xsl:if>
      </xsl:template>

      <xsl:template match="null-node()" mode="value">
        <xsl:value-of select="'null'"/>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

      <xsl:template match="SomeProperty/LowProperty1">
        <xsl:apply-templates select="." mode="name"/>
        <xsl:text>"bar"</xsl:text>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

    </xsl:stylesheet>,
    $stuff
  )
let$stuff:=
文件{
对象节点{
“SomeProperty”:对象节点{
“LowProperty1”:“一些字符串”,
“LowProperty2”:“一些字符串”,
“LowProperty3”:数组节点{“某些字符串1”,“某些字符串2”}
}
}
}
让$target:=xdmp:unpath(“/EvenLowerProperty/LowerProperty1”,(),$stuff)
返回
xdmp:xslt-eval(
"": 
""
, 
, 
,
,
,
, 
“酒吧”
, 
,
美元的东西
)

我的灵感来源于@MadsHansen发现的
xdmp:dialogue=“1.0-ml”
选项,该选项为我的另一个答案创建了一个更地道的版本。使用此XSLT,您可以维护使用MarkLogic JSON XPath扩展创建模板的能力(即:
match=“SomeProperty/LowProperty1”

这里的区别在于,不是一开始就转换成
json:object
XML,而是最初维护原生json对象,并且在转换过程中只转换成
json:object
。最后,所有内容都转换回本机。唯一的缺点是,在模板中构造新的json时,需要使用
json:object
XML,或者将本机构造函数包装在
xdmp:from-json()
中:


酒吧
{xdmp:来自json(
对象节点{“foo”:“bar”}
)}
还请注意,本机JSON语法仅在与
xdmp:xslt eval
-本机sy一起使用时有效
let $stuff :=
  document {
    object-node {
      "SomeProperty": object-node {
        "LowProperty1":"some string", 
        "LowProperty2":"some string", 
        "LowProperty3": array-node { "some string 1", "some string 2"}
      }
    }
  }

let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
  xdmp:xslt-eval(
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
        xdmp:dialect="1.0-ml">

      <xsl:output method="text"/>

      <xsl:variable name="lcurly" select="'&#123;'"/>
      <xsl:variable name="rcurly" select="'&#125;'"/>

      <xsl:template match="node()">
      <xsl:apply-templates select="." mode="name"/>
        <xsl:apply-templates select="." mode="value"/>
      </xsl:template>

      <xsl:template match="array-node()/node()">
        <xsl:apply-templates select="." mode="value"/>
      </xsl:template>

      <xsl:template match="node()" mode="name">
        <xsl:if test="string(node-name(.))!=''">"<xsl:value-of select="node-name(.)"/>": </xsl:if>
      </xsl:template>

      <xsl:template match="text()" mode="value">
        <xsl:text>"</xsl:text><xsl:value-of select="."/><xsl:text>"</xsl:text>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

      <xsl:template match="number-node() | boolean-node()" mode="value">
        <xsl:value-of select="."/>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

      <xsl:template match="object-node()" mode="value">
        <xsl:value-of select="$lcurly"/>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of select="$rcurly"/> 
         <xsl:if test="following-sibling::node()">,</xsl:if>
      </xsl:template>

      <xsl:template match="array-node()/object-node()" mode="value">
        <xsl:value-of select="$lcurly"/>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of select="$rcurly"/>
        <xsl:if test="following-sibling::node()">,</xsl:if>
      </xsl:template>

      <xsl:template match="array-node()" mode="value">
        <xsl:value-of select="'['"/>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of select="']'"/> 
        <xsl:if test="following-sibling::node()">,</xsl:if>
      </xsl:template>

      <xsl:template match="null-node()" mode="value">
        <xsl:value-of select="'null'"/>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

      <xsl:template match="SomeProperty/LowProperty1">
        <xsl:apply-templates select="." mode="name"/>
        <xsl:text>"bar"</xsl:text>
        <xsl:if test="following-sibling::node()">, </xsl:if>
      </xsl:template>

    </xsl:stylesheet>,
    $stuff
  )
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"
    xmlns:json="http://marklogic.com/xdmp/json"
    xmlns:xdmp="http://marklogic.com/xdmp"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xdmp:dialect="1.0-ml">

    <!-- XML -->
    <xsl:template match="SomeProperty/LowProperty1">
      <xsl:text>bar</xsl:text>
    </xsl:template>     

    <!-- Native JSON syntax -->
    <xsl:template match="SomeProperty/LowProperty2">
      {xdmp:from-json(
        object-node { "foo" : "bar" }
      )}
    </xsl:template>  

    <!-- Conversion handling -->

    <xsl:template match="/">
      <xsl:variable name="result" as="node()">
        <xsl:apply-templates select="@*|node()"/>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="namespace-uri-from-QName($result/node-name(.)) = 'http://marklogic.com/xdmp/json'">
          <xsl:sequence select="xdmp:to-json(json:object($result))"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$result"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

    <!-- Identity templates below -->

    <xsl:template name="json:value">
      <xsl:variable name="result" as="node()">
        <xsl:apply-templates select="."/>
      </xsl:variable>        
      <json:value>                       
        <xsl:if test=". instance of number-node()">
          <xsl:attribute name="xsi:type">
            <xsl:value-of select="xs:QName('xs:integer')"/>
          </xsl:attribute>
        </xsl:if>
        <xsl:sequence select="$result"/>
      </json:value>
    </xsl:template>

    <xsl:template match="object-node()"> 
      <json:object>
        <xsl:for-each select="node()">
          <json:entry key="{{ name(.) }}">
            <xsl:call-template name="json:value"/>
          </json:entry>
        </xsl:for-each>
      </json:object>
    </xsl:template> 

    <xsl:template match="array-node()"> 
      <json:array>        
        <xsl:for-each select="node()">
          <xsl:call-template name="json:value"/>
        </xsl:for-each>
      </json:array>
    </xsl:template> 

    <xsl:template match="number-node()">
      <xsl:value-of select="."/>
    </xsl:template>

    <xsl:template match="node()|@*" priority="-1">
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
xquery version "1.0-ml";

let $stuff :=
  document {
    object-node {
      "SomeProperty": object-node {
        "LowProperty1":"some string", 
        "LowProperty2":"some string", 
        "LowProperty3": array-node { "some string 1", "some string 2"}
      }
    }
  }

let $target := xdmp:unpath("/EvenLowerProperty/LowestProperty1", (), $stuff)
return
  xdmp:xslt-eval(
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="2.0" xdmp:dialect="1.0-ml"
        xmlns:json="http://marklogic.com/xdmp/json">

      <xsl:template match="object-node()">
        <xsl:variable name="this" select="json:object()"/>
        <xsl:for-each select="node()">
          <xsl:variable name="contents" as="item()*">
            <xsl:apply-templates select="."/>
          </xsl:variable>
          <xsl:sequence select="map:put($this, name(.), $contents)"/>
        </xsl:for-each>
        <xsl:sequence select="xdmp:to-json($this)"/>
      </xsl:template>

      <xsl:template match="array-node()">
        <xsl:variable name="contents" as="item()*">
          <xsl:apply-templates select="node()"/>
        </xsl:variable>
        <xsl:sequence select="json:to-array($contents)"/>
      </xsl:template>

      <xsl:template match="text()">
        <xsl:sequence select="."/>
      </xsl:template>

      <xsl:template match="SomeProperty/LowProperty1">
        <xsl:text>foo</xsl:text>
      </xsl:template>
    </xsl:stylesheet>,
    $stuff
  )