在 JSON 数组中搜索特定值,然后合并为 XML

search specific value in JSON array then merge as XML

我刚开始在不同的数据库中搜索 JSON 并转换:

/doc1.json:

{
    "seller": "s1",
    "product": [
        "football",
        "basketball"
    ],
    "sales": [
        {
            "football": 60,
            "basketball": [
                {
                    "c1": 76,
                    "c2": 90
                }
            ]
        }
    ]
}

/doc2.json

{
    "seller": "s2",
    "product": ["football"],
    "sales": [
        {"football": 80}
    ]
}

我发现产品只包含一个特定项目(如果 JavaScript 我会更好),与销售合并,并将结果转换为 XML(使用 XSLT?)。但我并没有走得太远。首先,我尝试 MarkLogic JS:

cts.search(cts.jsonPropertyValueQuery('product', 'football'))

和许多其他方法,它总是 returns 2 个文档。我需要使用模块函数而不是硬编码 JSON 值。

在此先感谢您的帮助。

var doc = cts.doc("/doc1.json");
 var foo = fn.head(xdmp.unquote(
    `
    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\n\
        version="2.0">\n\

        <xsl:template match="/">\n\
            <root>\n\
                <xsl:element name="products">\n\
                    <xsl:for-each select="product">\n\
                        <xsl:element name="product">\n\
                            <xsl:variable name="value" select="."/>\n\
                            <xsl:value-of select="."/>\n\
                            <xsl:element name="sales">\n\
                                <xsl:for-each select="/sales/*[name() = $value]">\n\
                                    <xsl:copy select="."/>\n\
                                </xsl:for-each>\n\
                            </xsl:element>\n\
                        </xsl:element>\n\
                    </xsl:for-each>\n\
                </xsl:element>\n\
            </root>\n\
        </xsl:template>\n\

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

    </xsl:transform>\n\
    `));

    xdmp.xsltEval(foo, doc);


/doc2.json 应该是:

<root>
  <products>
    <product>football<sales>80</sales>       
    </product>
  </products>
</root>

/doc1.json 应该是:

<root>
  <products>
    <product>football<sales>60</sales></product>
    <product>basketball<sales>
      <c1>76</c1>
      <c2>90</c2>
    </sales></product>
  </products>
</root>

SJS:

function valueGetDocUri (collection, propertyPath, propertyValue) {
    const result =[];
    cts.uris("", null, cts.collectionQuery(collection)).toArray().forEach(uri => {
        (cts.values(cts.pathReference(propertyPath), null, null, cts.documentQuery(uri)) == propertyValue) ? result.push(uri) : {}
    });
    return result;
};

执行(这里我有 /indicator 上的路径索引):

const try3 = ['profile',  '/indicator',  'SMA'];

valueGetDocUri(...try3)

在 MarkLogic 中,每个 JSON 数组值都是其关联的 属性 的值。 cts.values + cts.pathReference 将搜索仅包含指定值词典的路径,在本例中为 'SMA'.

示例文档:

  • /profile/multi-indicator.json
{
    "symbol": "USDEUR",
    "date": "2021-08-17",
    "indicator": [
        "BBANDS",       
        "SMA",
        "MACD"
    ] ,
    "riskAdjusted": [
        { "indicator": [  
              {
                "Treynor": 7.19,
                "Jensen": 5.13
              }
           ]
        }
    ],
    "technicalAnalysis": [
        {
            "SMA": 0.8426,
            "MACD": [
                {
                    "MACD_Signal": -0.0007,
                    "MACD_Hist": 0.0026,
                    "MACD": 0.0020
                }
            ],
            "BBANDS": [
                {
                    "realLowerBand": 0.8379,
                    "realUpperBand": 0.8593,
                    "realMiddleBand": 0.8486
                }
            ]
        }
    ]
}

To solve the 2nd problem (in your case, replace the params object with yours: match = ‘product’; merge = ‘sales’…etc)

let doc = cts.doc("/profile/multi-indicator.json");
var params = {};
params.match = 'indicator';
params.merge = 'technicalAnalysis'; 

const implJtoX = fn.head(xdmp.unquote(
`
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="schema://fc.fasset/profile"
exclude-result-prefixes="xs" 
version="2.0">

<xsl:param name="match"/>
<xsl:param name="merge"/>

<xsl:template match="/">
    <profile>
        <xsl:apply-templates/>
        <xsl:element name="algorithm">
            <xsl:for-each select="*[name() eq $match]">
                <xsl:element name="indicator">
                    <xsl:variable name="eName" select="normalize-space(data(.))"/>
                    <xsl:value-of select="$eName"/>
                    <xsl:element name="technical">
                        <xsl:for-each
                            select="/*[name() eq $merge]/*[name() eq $eName]">
                            <xsl:choose>
                                <xsl:when test="*">
                                    <xsl:for-each select="./node()">
                                        <xsl:element name="{normalize-space(name(.))}">
                                            <xsl:value-of select="."/>
                                        </xsl:element>
                                    </xsl:for-each>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select="."/>
                                </xsl:otherwise>
                            </xsl:choose> 
                        </xsl:for-each> 
                    </xsl:element>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </profile>
</xsl:template>

<xsl:template match="node()">
    <xsl:apply-templates/>
</xsl:template>

</xsl:transform>
`));

const JtoX = xdmp.xsltEval(implJtoX, doc, params);
JtoX

从工程的角度来看,您 调用 JavaScript 函数和 XSL,传入搜索结果中的迭代文档和转换参数。我让您打包 SJS 和 XSL 模块。如果你完成这个 运行-up,我不明白为什么你不应该尝试多名称空间 XSL 转换 (with xdmp:dialect="1.0-ml" [the MarkLogic 上的责任] 或 没有 [XSLT 编辑器上的责任])。转换后的模型如下:

<prof:profile xmlns:prof="schema://fc.fasset/profile" xmlns:meta="schema://fc.fasset/svm/meta">
  <meta:header>
    <meta:IDV1>/svm/d91810d-158-494-ad3-e5afc35a5.xml</meta:IDV1>
    <meta:symbol>USDEUR</meta:symbol>
    <meta:dateSeries>2021-08-17</meta:dateSeries>
    <meta:performance>
        <meta:Treynor>7.19</meta:Treynor>
        <meta:Jensen>5.13</meta:Jensen>
    </meta:performance>
  </meta:header>
  <prof:algorithm>
    <prof:indicator>BBANDS<prof:technical>
        <prof:realLowerBand>0.8379</prof:realLowerBand>
        <prof:realUpperBand>0.8593</prof:realUpperBand>
        <prof:realMiddleBand>0.8486</prof:realMiddleBand>
    </prof:technical></prof:indicator>
    <prof:indicator>SMA<prof:technical>0.8426</prof:technical></prof:indicator>
    <prof:indicator>MACD<prof:technical>
        <prof:MACD_Signal>-0.0007</prof:MACD_Signal>
        <prof:MACD_Hist>0.0026</prof:MACD_Hist>
        <prof:MACD>0.002</prof:MACD>
    </prof:technical></prof:indicator>
  </prof:algorithm>
</prof:profile>

Of course, the JavaScript module has its XQuery equivalent. They can interchangeably evaluate each other or XSL module. Minute difference between SJS and XQY invocation notwithstanding, XSLT is well suited to such XML transformation tasks.